(一)
慎用多重继承,因为那样的话可能会造成歧义。。
<pre name="code" class="cpp">class BorrowableItem { public: void checkOut(); }; class ElectronicGadet { private: bool checkOut() const; }; class MP3Player : public BorrowableItem <pre name="code" class="cpp" style="font-size: 16px; line-height: 28px;"> public ElectronicGadet
{...}; MP3Player mp; mp.checkOut();//歧义,调用的是哪个checkOut?
这与C++用来解析重载函数调用的规则相符:在看到是否有个函数可取用之前,C++首先确认这个函数对此调用之言是最佳匹配。找到最佳匹配函数后才检验其可取用性。本例中连最佳匹配都出现歧义,所以是否可取用就免谈了。
所以解决办法:
mp.BorrowableItem::checkOut();
你当然也可以明确调用mp.ElectronicGadget::checkOut(),但然后你会获得一个“尝试调用private成员函数”的错误。
(二)
如果base
classes在继承体系中又有更高级的base classes,非virtual继承会造成多份base
class成员变量。这时要用virtual继承。为了这样做,你必须令所有直接继承自它的classes采用“virtual继承”
下面这种缺省行为会导致类IOFile的对象中有两份File的副本:
class File{...}; class InputFile: public File {...}; class OutputFile: public File{...}; class IOFile: public InputFile, public OutputFile {...};
下面这种虚继承的行为会导致类IOFile的对象中只有一份File的副本:
class File{...}; class InputFile: virtual public File {...}; class OutputFile: virtual public File{...}; class IOFile: public InputFile public OutputFile {...};
(1)使用virtual继承的那些classes所产生的对象往往比使用non-virtual继承的兄弟们体积大,访问virtual base classes的成员变量时,也比访问non-virtual base
classes成员变量速度慢。
(2)class若派生自virtual
base class而需要初始化,必须认知其virtual bases----不论那些bases距离多远。
(3)当一个新的derived
class加入继承体系中,它必须承担起virtual bases(不论直接或间接)的初始化工作。
(4)我们对virtual继承的忠告:第一,非必要不要使用virtual
bases;
第二,如果必须使用virtual bases,尽可能避免在其中放置数据。
(三)
多重继承的确有正当用途。其中一个情节涉及“public继承某个interface
class”(接口)和“private继承某个协助实现的class”(实现)的两相结合。例子:
class Iperson { public: virtual ~IPerson(); virtual std::string name() const=0; virtual std::string birthDate() const=0; }; class DatabaseID { ... }; //稍后被使用 class PersonInfo { //这个class有若干有用函数,可用以实现IPerson接口 explicit PersonInfo(DatabaseID pid); virtual ~PersonInfo(); virtual const char* theName() const; virtual const char* theBirthDate() const; virtual const char* valueDelimOpen() const; virtual const char* valueDelimClose() const; }; const char* PersonInfo::valueDelimOpen() const { return "["; //缺省的起始符号 } const char* PersonInfo::valueDelimClose() const { return "]"; //缺省的结尾符号 } const char* PersonInfo::theName() const { static char value[Max_Formatted_Field_Value_Length]; //保留缓冲区给返回值使用:static,自动初始化为“全0” std::strcpy(value, valueDelimOpen()); //写入起始符号 ... //将value内的字符串附到这个对象的name成员变量中 std::strcat(value, valueDelimClose()); //写入结尾符号 return value; } 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() const { //实现必要的IPerson成员函数 return PersonInfo::theBirthDate(); } private: const char* valueDelimOpen() const { //重新定义继承而来的virtual“界限函数” return ""; } const char* valueDelimClose() const { return ""; } };
(1)所以theName返回的结果不仅仅取决于PersonInfo也取决于从PersonInfo派生下去的classes。
(2)Cperson和personInfo的关系是,PersonInfo刚好有若干函数可帮助Cperson比较容易实现出来。因此它们的关系是is-implemented-in-term-of(根据某物实现出)。这种关系可以两种技术实现:复合和private继承。一般复合比较受欢迎,但是在本例之中Cperson要重新定义valueDelimOpen()和valueDelimClose(),所以直接的解法是private继承。
(3) 多重继承&单一继承比较,它通常比较复杂,使用上也比较难以理解,所以如果你有个单继承的设计方案,而它大约等价于一个多重继承设计方案,那么单一继承设计方案几乎一定比较受欢迎。
(4) 如果你唯一能够提出的设计方案涉及多重继承,你应该更努力想一想:几乎可以说一定会有某些方案让单一继承行得同。
(5) 然而多继承有时候的确是完成任务之最简洁、最易维护、最合理得做法,果真如此就别害怕使用它。
请记住:
(1)多重继承比单一继承复杂。它可能导致新的歧义性,以及对virtual继承的需要。
(2)virtual继承会增加大小、速度、初始化(及赋值)复杂度等等成本。如果virtual base classes不带任何数据,将是最具实用价值的情况。
(3)多重继承的确有正当用途。其中一个情节涉及“public继承某个Interface class”和“private继承某个协助实现的class”的两相组合。
Effective C++:条款40:明智而审慎地使用多重继承