Effectiv C++ (06)
来源:互联网 发布:珠海设计院使用软件 编辑:程序博客网 时间:2024/06/08 11:15
条款34:区分接口继承和实现继承
Class Shape
{
Public:
Virtual void draw() const = 0;
Virtual void error(const string &msg);
Int objectID() const;
};
Class Rectangle : public Shape
{
};
Class Ellipse:public Shape
{
};
成员函数的接口总是被继承
纯虚函数:
必须被继承了的子类实现
声明virtualfunc = 0;是为了子类只继承接口
Shape *ps = new Shape; Error
Shape *ps1 = new Rectangle; OK
Ps1->draw();
Shape *ps2 = new Ellipse;
Ps2->draw();
Ps1->Shape::draw(); 调用Shape::draw
Ps2->Shape::draw(); 调用Shape::draw
声明非virtual函数是令子类继承函数的接口以及一份强制性的实现。任何子类不不应该尝试改变其实现方式。不应该在子类中重定义。
纯虚函数:只继承接口
虚函数: 继承接口和一份缺省实现
普通函数:继承接口和一份强制实现
常犯的错误:
将所有函数都声明为普通成员函数(非虚、非纯虚),除非它不会作为父类被继承
将所有成员函数声明为virtual,除非它是interfaceclass
接口继承和实现继承不一样
条款35:考虑virtual函数以外的其他选择
Class GameCharacter
{
Public:
Int healthValue() const
{
Int retVal = doHealthValue();
Return retVal;
}
Private:
Virtual int doHealthValue() const
{
}
};
通过非虚函数间接调用private virtual函数。NVI(non-virtual interface)
条款36:绝不重新定义继承而来的非虚函数
如果要改,说明子类“is-a”父类,不成立。要么不能public继承,要么就需要将该方法声明为virtual。
对于析构函数,由于每一个类的析构过程与父类不尽相同,因此需要声明为virtual
条款37:绝不重新定义继承而来的缺省参数值
假设继承而来的函数分为:虚函数和非虚函数。由于不能重写非虚函数,因此框定:继承一个带有缺省参数的virtual函数。
virtual函数是动态绑定的,而缺省参数值是静态绑定的!
对象的静态绑定,它在程序中被声明时所采用的类型。
Class Shape
{
Public:
Enum ShapeColor{Red, Green, Blue};
Virtual void draw(ShapeColor color = Red) const = 0;
};
Class Rectangle : public Shape
{
Public:
Virtual void draw(ShapeColor color = Green) const = 0;
};
Class Circle : public Shape
{
Public:
Virtual void draw(ShapeColor color) const = 0; // 不带缺省参数
};
// 静态绑定为Shape类型
Shape *ps;
Shape *pc = new Circle;
Shape *pr = new Rectangle;
// 动态绑定
Ps = pc; // Circle
Ps = pr; // Rectangle
Pc->draw(Shape::Red); // 调用Circle::draw(Shape::Red)
Ps->draw(Shape::Red); // 调用Rectangle::draw(Shape::Red)
//带有缺省参数。缺省参数静态绑定
Pr->draw(); // 调用Rectangle::draw(Shape::Red),此例中pr是Rectangle类型,因此会调//用Rectangle::draw,然而其缺省参数应该是Green,但由于pr的静态类型是Shape,所以//缺省值来自于Shape而不是Rectangle。结果比较诡异,各贡献一半!
//引用,结果也一样
缺省参数值是静态绑定。即什么类型(Shape)的指针或引用,被赋予那个类型(Shape)的缺省值,而不考虑其真实类型(Circlr Rectangle).
条款38:通过复合模塑出“has-a”或“根据某物实现出”
复合:has-a或is-implement-in-terms-of(根据某物实现出)
复合:在应用域:has-a;在实现域:根据某物实现出
条款39:明智而审慎的使用private继承
如果是private继承,编译器不会自动将一个子类转换为一个父类对象,这与public继承不同
private继承自所有父类的东西,在子类都是private的
如果classD私有继承classB,目的是采用classB内已经备妥的某些特性
private继承纯粹是一种实现技术。
private继承意味着只有实现部分被继承,接口部分被略去。
private继承意味着“根据某物实现出”
尽可能使用复用,必要时才使用private。何时必要?当protected、virtual成员牵涉进来时。
对于class内部为空的,sizeof(ClassEmty)并不是0,而是被安插了1个char
Class Empty
{
};
Class HoldsAnInt
{
Int num;
Empty e;
};
//Sizeof(HoldsAnInt)可能是8
Class HoldsAnInt : private Empty
{
Int num;
};
//Sizeof(HoldsAnInt) = 4
条款40:明智而审慎的使用多重继承
Class BorrowableItem
{
Public:
Void checkOut();
};
Class ElectronicGadget
{
Private:
Bool checkOut()const;
};
Class MP3Player:
Public BorrowableItem,
Pubilc ElectronicGadget
{
};
MP3Player mp;
Mp.checkOut(); // 歧义
在看到是否有函数可用之前,C++首先确认这个函数对此调用而言是最佳匹配。
找出最佳匹配函数之后,才检验其可用性(即接口访问限制:public、private、const…)上例中两个函数有相同的匹配程度,造成了歧义。没有所谓的最佳匹配,因此可用性还没有检查
明白指出调用哪一个:
mp.BorrowableItem::chechOut();
Class File
{};
Class InputFile: public File{};
Class OutputFile:public File{};
Class IOFile: public InputFile, public OutputFile{};
如果子类继承的父类有一个共同的祖先:C++缺省的做法是保留两份祖先的成员。
如果想让子类只有一份祖先的成员,需要将祖先设为virtual base class
Class File
{};
Class InputFile: virtual public File{};
Class OutputFile:virtual public File{};
Class IOFile: public InputFile, public OutputFile{};
访问virtual继承的代价大!需要为virtual继承付出代价
virtual继承的初始化复杂、不直观:责任由继承体系中最底层的class(IOFile)负责
建议:
非必要不要使用virtual继承
如果必须使用virtual继承,应避免在其中放置数据(不用担心因初始化和赋值而带来的诡异事)。
virtual继承会增加大小、速度、初始化(赋值)复杂度
- Effectiv C++ (06)
- Effectiv C++ (07)
- Effectiv C++ (08)
- 警惕C++隐式转换(More Effectiv C++_5(运算符))
- [effectiv c++]条款21:返回reference 与 返回object
- EFFECTIV C++
- [effectiv c++]条款2:尽量以const,enum,inline替换#define(static,const定义式)
- [effectiv c++]条款5:了解C++默默编写并调用哪些函数
- [effectiv c++]条款11:operator=的自我赋值与异常处理
- [effectiv c++]条款20:pass-by-reference-to-const 与 pass-by-value(解决slicing问题)
- [effectiv c++]条款25:考虑写出一个不抛出异常的swap函数
- [effectiv c++]条款33:避免遮掩继承而来的名称(重载,重写,重定义)
- [effectiv c++]条款39:明智而审慎地使用private继承
- [effectiv c++]条款37:绝不重新定义继承而来的缺省参数值(动态绑定,静态绑定,多态性)
- Effectiv Java 读书笔记 Item 1
- Effectiv STL 37 Use accumulate or for_each to summarize ranges
- Effectiv C++条款31 将文件间的编译依存关系降至最低 Handle Class和Interface Class完整实现
- Effectiv C++条款31 将文件间的编译依存关系降至最低 Handle Class和Interface Class完整实现
- SSH项目多数据源的配置
- android手机抓包
- 【火影之卡卡西动漫主题】
- 转换到 COFF 期间失败: 文件无效或损坏——WTL从低版本到高版本转换2个fatal error处理
- IOS编译静态库的心得
- Effectiv C++ (06)
- linux下错误的捕获:errno和strerror的使用
- Html5 表单验证和style css验证支持
- ubuntu 12.04 时间面板乱码问题修正
- Let's Go!
- linux+shell编程 笔记 3
- 基本数据的自动拆箱与装箱及其细节问题
- 使用connection对象创建SQL server和ODBC的数据源连接
- 层层递进Struts1(二)之Struts1简介及登录实例