1.多重继承的缺点:
(1)从一个以上的基类继承相同名称,可能导致歧义,使用时应当使用域作用符限定具体是哪个基类的成员。注意:在这种歧义中,C++用来解析重载函数调用的规则在起作用——在看到是否有个函数可取用之前,首先确定这个函数对此调用来说是最佳匹配,找到最佳匹配函数后才检验其可取用性。
(2)如个一个基类有多个派生类,而更深层次的派生类从这些派生类中派生出来,基类如何构成这更深层次的派生类呢?
- 从每一条路径中继承派生类,因而该派生类对象有两份甚至多份“基类”对象;
- 但从基类中继承而来的这些对象不能重复。
C++缺省支持第一种方案,但是也可以支持第二种方案,此时应当使基类成为虚基类,因此应当使直接继承自基类的派生类都采用虚继承。STL中的输出输出流就采用类似的方法(采用类模板)。
2.实际使用中应尽量避免多重继承,而采用相同功能的单一继承方案。
3.尽量以虚继承的方式使用public继承。
但是虚继承有缺点:
- (1)使用虚继承的派生类往往比非虚继承的类体积大,成员变量的访问速度也较慢。
- (2)虚基类初始化问题:虚基类的初始化由继承体系中的最低层负责,这表示:
- 1)派生自虚基类的类初始化时必须认知这个虚基类;
- 2)当一个新的派生类加入到继承体系中,必须承担起虚基类(直接或间接)的初始化责任。
因此,对于虚基类,建议有:
- (1)非必要不使用虚基类;
- (2)如果必须使用,尽可能不在其中放置数据。(类似Java和.NET中的接口。)
4.实例:个人信息管理的例子:
假设有一个IPerson类表示“人”:
class IPerson{
public:
virtual ~IPerson();
virtual std::string name() const = 0;
virtual std::string birthDate() const = 0;
}
它使用工厂函数来返回派生类对象的(智能)指针:
std::tr1::shared_ptr<IPerson> makePerson(DatabaseID personIdentifier);
另外拥有一个PersonInfo类,它与数据库相关:
class PersonInfo{
public:
explicit PersonInfo(DatabaseID pid);
virtual ~PersonInfo();
virtual const char* theName() const;
virtual const char* theBirthDate() const;
...
private:
virtual const char* valueDelimOpen() const; //“开始”符号,用于姓名的输出
virtual const char* valueDelimClose() const; //“结束”符号
...
};
在这个PersonInfo::theName()函数中,将调用“开始”和“结束”符号的函数来输出经过修饰的名字:
const char* PersonInfo::theName() const{
static char value[Max_Formatted_Field_Value_Length]; //保留缓冲区给返回值使用。注意由于缓冲区是static的,会被自动初始化为“全部是0”
std::strcpy(value, valueDelimOpen()); //写入起始符号
... //添加姓名
std::strcat(value, valueDelimClose()); //写入结束符号
return value;
}
在这个类中,valueDelimOpen()和valueDelimClose()是虚函数,因而此函数的返回结果同时取决于PersonInfo和其派生类。对于IPerson的派生类,如CPerson类,作为“人”的具体实现者,应当改写name()和birthDate(),使其返回值中不带有开始和结束符号。
因此,CPerson应当使用PersonInfo中的功能(根据某物实现出),并且如果使用复合,由于PersonInfo类是虚基类,无法直接生成对象,则应当使用私有继承;同时对IPerson采用公有继承,以继承它的接口。结合起来应当是: