Effective C++:条款40:明智而审慎地使用多重继承

(一)

慎用多重继承,因为那样的话可能会造成歧义。。

<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:明智而审慎地使用多重继承

时间: 2024-10-20 02:09:57

Effective C++:条款40:明智而审慎地使用多重继承的相关文章

Effective C++ Item 40 明智而审慎地使用多重继承

本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie 经验:多重继承比单一继承复杂.它可能导致新的歧义性,以及对 virtual 继承的须要 演示样例: class BorrowableItem{ public: void checkOut(); }; class ElectronicGadget{ private: bool checkOut() const; }; class MP3Player: public BorrowableIte

Effective C++ 条款40 明确而审慎地使用多重继承

1. 使用多重继承,派生类有可能从一个以上的基类继承相同名称,这回导致歧义.即使来自不同基类的相同名称各自的访问级别不同,也有仍然有可能造成歧义,因为C++的名称查找法则是先查找最佳匹配,然后再检查可取用性.可以使用作用域操作符明确指定所使用的名字属于那一个类. 2. 如果多重继承继承的一个以上基类又出现继承自同一个类的情况,将会导致"钻石型继承",即A->B;A->C;B,C->D,此时就要面对是否将A设为虚基类的选择.如果不采用虚继承,那么A在D中会存在两份实体(

Effective C++ 条款39 明智而审慎地使用private继承

1. public继承表明is-a的关系,要求接口的完全继承,而private继承表明"根据某物实现出的关系",要求仅仅继承实现,private继承有两个规则: 1). 经由private继承而来的基类的所有成员在派生类中都会变成private属性 2). 由于1),编译器不允许将派生类转为基类以防止对派生类private成员的非法访问. 2. 由条款38,private继承和复合具有相同作用——"根据某物实现出".两者之间,要尽可能使用复合,除非必要情况.必要情况

Effective C++ Item 39 明智而审慎地使用 private 继承

本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie 经验:private 继承意味 is-implemented-in-terms of.它通常比 composition 的级别低. 但是当 derived class 需要访问 protected base class 的成员,或需要重新定义继承而来的 virtual 函数时,这么设计是合理的 经验:和 composition 不同, private 继承可以造成 empty base 最

Effective C++ -----条款40:明智而审慎地使用多重继承

多重继承比单一继承复杂.它可能导致新的歧义性,以及对virtual继承的需要. virtual继承会增加大小.速度.初始化(及赋值)复杂度等等成本.如果virtual base classes不带任何数据,将是最具实用价值的情况. 多重继承的确有正当用途.其中一个情节涉及“public继承某个Interface class"和”private继承某个协助实现的class“的两相组合.

条款40:明智而审慎的使用多重继承

多重继承(multiple inheritance:MI)在C++阵营中有不同主张.一个是:如果认为单一继承(single inheritance:SI)是好的,那么多重继承一定也是好的:另一个是:单一继承是好的,但是多重继承不是.本条款主要让大家了解这两个观点. 当使用MI时,程序可能从一个以上base classes继承相同名称(函数.typedef等),这会导致歧义(ambiguity) clas BorrowableItem{ public: void checkOut(); -- };

More Effective C++ 条款14 明智运用exception specifications

1. Exception specifications作为函数声明的一部分,用于指出(并不能限制)函数可能会抛出的异常函数.C++规定,一个拥有exception specification的函数指针只能被赋予一个有着相同或更为局限的exception specification的函数地址,因而编译器要保证"在函数指针传递之际检验exception specifications".(但visual studio 2013不支持此项要求) 2. 当函数抛出exception specif

Effective C++条款40

本节条款讲述了多重继承的使用 多重继承一般情况下用的很少,原因在于多重继承容易出现程序错误.以下去两个典型的调用错误: 第一种错误如下代码: #include<iostream> using namespace std; class B { public: virtual int m(){} }; class C { public: virtual int m(){}; }; class D :public B,private C { public: }; int main() { D d;

effective c++条款32~40“继承与面向对象设计”整理

条款32:确定你的public继承塑模出is-a关系 以C++进行面向对象编程,最重要的一个规则是:public inheritance(公有继承)意味is-a(是一种)的关系. 在C++领域中,任何函数如果期望获得一个类型为基类的实参(而不管是传指针或是引用),都也愿意接受一个派生类对象(而不管是传指针或是引用).(只对public继承才成立.)好的接口可以防止无效的代码通过编译,因此你应该宁可采取"在编译期拒绝"的设计,而不是"运行期才侦测"的设计.is a并不