Effective C++(条款39-40)
来源:互联网 发布:知乎 大野智 编辑:程序博客网 时间:2024/05/29 03:55
条款39:明智而审慎的使用private继承
private继承意味着is-implemented-in-terms-of(根据某物实现出)。它通常比复合的级别低。但是当继承类需要访问基类的protected成员,或需要重新定义继承而来的virtual函数时,这么设计是合理的。
先了解一下private继承的特点:
1、如果类之间的继承关系是private,编译器不会将一个继承类对象转换为一个基类对象,这意味着private继承并不是is-a的关系。
class Person { protected: string name; }; class Student:private Person { private: string schoolNumber; }; void eat(const Person& p) { cout<<"eat"<<endl; } void study(const Student& s) { cout<<"study"<<endl; } int main() { Person p1; eat(p1); Student s1; study(s1); // eat(s1);错误 return 0; }
2、由private base class继承而来的所有成员,在继承类中都会变成private属性。纵使他们在基类中原本是protected或public属性。
因此,private继承意味着:根据某物实现。这与前面条款介绍的复合很类似。private继承意味只有实现部分被继承,接口部分应该被略去。继承类根据基类实现而得,不存在其他的什么关系了。在大多数时候,我们应该使用复合,而不是private继承来实现这种功能。但是当有protected成员和虚函数牵扯进来的时候,我们又不得不用private继承。
其次,与复合相比,private可以使空基类的最优化。先看一个例子:
//定义一个空基类 class Empty{}; class HoldsAnInt { private: int x; Empty e;//复合 };
在vs中,sizeof(int)为4,sizeof(Empty)为1,sizeof(HoldsAnInt)为8这似乎有悖于我们学过常理:首先,类的大小取决于其数据成员的大小。sizeof(Empty)应该为0,但是由于在编译器会将它的大小设为1,而“齐位需求”会将它放大为1个int,所以,izeof(HoldsAnInt)为8。但是,如果使用的是private继承来实现,就不存在这种问题了:sizeof(HoldsAnInt)只有一个int的大小:4。
总之,private继承意味着根据某物实现。当派生类需要访问基类的的受保护成员或者重新定义虚函数时,我们才使用它。而且private继承可以是得空基类最优化,如果在开发中需要是得对象尺寸最小,那么也用得着它。“明智而审慎的使用private继承”意味着,在考虑过所有其他方案后,如果仍然认为private继承是“表现程序内两个类之间的关系”的最佳办法,这才使用它。
条款40:明智而审慎的使用多重继承
1、多重继承比单一继承复杂。它可能导致新的歧义性,以及对virtual继承的需要
当使用多重继承(MI)时,程序可能从一个以上base classes继承相同名称(函数、typedef等),这会导致歧义:
clas BorrowableItem{ public: void checkOut(); …… }; class ElectronicGadgent{ private: bool checkOut() const; …… }; class MP3Player: public BorrowableItem, public ElectronicGadget { …… }; MP3Player mp; mp.checkOut();//歧义,调用哪个checkOut
为了解决歧义,必须指明调用哪一个base class内的函数
mp.BorrowableItem::checkOut();
多重继承还可能会导致“钻石型多重继承”
class File{……}; class InputFile: public File{……}; class OutputFile: public File{……}; class IOFile:public InputFile, public OutputFile {……};
在上面这个继承体系中,IOFile会有File的两重拷贝,因为InputFile和OutFile都有一份拷贝,而IOFile有这两个class的拷贝。假如File class有个成员变量fileName,那么IOFile内有两个这样的数据。IOFile继承base classes,所以应该有两份,但是这两个变量是重复的,fileName只需一份即可。
C++在是否需要2份拷贝的辩论中没有立场,两个方案它都支持,缺省方案是第一个方案。如果要用第二个方案,那么就要让带此数据的class(即File)成为一个virtual base class。
从正确行为的观点看,public继承应该总是virtual、如果这是唯一一个观点,那么任何时候当你使用public继承时,请改用virtual public继承。
2、但是,virtual继承会增加大小、速度、初始化(及赋值)复杂度等等成本,如果virtual base class不带任何数据,将是最具实用价值的情况。
对virtual 继承的忠告:非必要不要使用virtual继承,平常请使用non-virtual继承;如果必须使用virtual继承,尽可能避免在其中放置数据。
3、多重继承的确有正当用途。其中一个情节设计“public继承某个类的接口”和“private继承某个协助实现的类”的组合
class IPerson{ //这个类指出需实现的接口 public: virtual ~IPerson(); virtual std::string name() const=0; virtual std::string birthDate() const=0; }; class DatabaseID{……};//稍后被使用:细节不重要 class PersonInfo{ //这个类有若干有用函数,可以以实现IPerson接口 public: explicit PersonInfo(DatabaseID pid); virtual ~PersonInfo(); virtual const char* theName() const; virtual const char* theBirthdayDate() const; …… private: virtual const char* valueDelimOpen() const; virtual const char* valueDelimClose() const; …… }; class CPerson: public IPerson, private PersonInfo{ //多重继承 public: explicit CPerson(DatabaseID pid): PersonInfo(pid){} virtual std::string name() const //实现必要的IPerson成员函数 { return PersonInfo::theName(); } virtual std::string birthDate() { return PersonInfo::theBirthDate(); } private: const char* valueDelimOpen() const{return "";} //重新定义继承的virtual成员函数 const char* valueDelimClose() const{return "";} };
多重继承只是面向对象的一个工具而已。和单一继承比较,它比较复杂,也难以理解,所以如果有一个单一继承方案和一个多重继承方案,那么单一继承方案比较受欢迎。但是如果通过多重继承可以完成任务,而且最简洁、最易维护、最合理,那么就不用怕使用它。
参考:http://blog.csdn.net/thefutureisour/article/details/7985263
http://blog.csdn.net/KangRoger/article/details/44161773
- Effective C++(条款39-40)
- 《Effective C++》:条款38-条款39
- Effective C++(条款1-4)
- Effective C++(条款5-12)
- Effective C++(条款13-17)
- Effective C++(条款18-25)
- Effective C++(条款26-31)
- Effective C++(条款32-34)
- Effective C++(条款35-38)
- Effective C++(条款41-44)
- Effective C++(条款45-47)
- Effective C++(条款48-50)
- Effective C++(条款51-55)
- Effective C++:条款01
- Effective C++:条款02
- Effective C++:条款03
- Effective C++:条款04
- Effective C++:条款05
- HttpURLConnection.getContentLength()为-1 解决方法
- @html.ActionLink的几种参数格式
- Intent.ACTION_TIME_TICK的正确用法
- iOS7程序后台运行
- Sql 查询执行过的Sql语句记录
- Effective C++(条款39-40)
- 【论文笔记】人脸关键点检测_简略版(时时更新中...)
- Python通过PIL获取图片主要颜色并和颜色库进行对比的方法
- CSS水平和垂直居中方式
- iOS多线程的初步研究(三)-- NSRunLoop
- Windows下使用python虚环境
- Linux2.6进程切换
- 开始学java
- C++学习之const详解篇