本文转载自:http://blog.csdn.net/wuliming_sc/article/details/3832583
多重继承
《C++ primer 3th》P794~798
为支持多继承,一个类的派生表:
class Bear : public ZooAnimal { ... };
被扩展成支持逗号分割的基类表。例如:
class Panda : public Bear, public Endangered { ... };
每个被列出的基类还必须指定其访问级别:public、protected或private之一。与单继承一样,只有当一个类的定义已经出现后,它才能被列在多继承的基类表中。
对于一个派生类的基类的数目,C++没有限制。实际情况下,两个基类是最常见的,一个基类常常用于表示一个公有抽象接口,另外一个基类提供私有实现。从三个或者更多个直接基类继承而来的派生类遵循mixin-based
设计风格,其中每个基类都表示该派生类完整接口的一个方面。
构造的次序
在多继承下,派生类含有每个基类的一个基类子对象。例如,当我们写:
Panda ying_yang;
时,ying_yang由一个Bear类子对象(它又含有一个ZooAnimal基类子对象)、一个Endangered类子对象,以及在Panda类中声明的非静态数据成员组成:
多继承Panda层次结构
基类构造函数被调用的顺序以类派生表中声明的顺序为准。例如对ying_yang来说,构造函数被调用的顺序是:Bear构造函数(因为Bear是从ZooAnimal派生的,所以在Bear构造函数执行之前,ZooAnimal的构造函数先被调用),Endangered构造函数,然后是Panda构造函数。
构造函数调用顺序不受基类在成员初始化表中是否存在以及被列出的顺序的影响,也即是说,如果Bear缺省构造函数被隐式调用,没有出现在成员初始化表中,如下所示:
// Bear缺省构造函数在Endangered的双参数构造函数之前被调用
Panda::Panda(): Endangered( Endangered::environment,Endangered::critical )
{ ... }
那么Bear的缺省构造函数仍然在显式列出的双参数Endangered构造函数之前被调用。类似地,析构函数调用顺序总是与构造函数顺序相反。在我们的例子中,析构函数调用顺序是:~Panda()、~Endangered()、~Bear(),最后是~ZooAnimal()。
同名成员函数的二义性
在单继承下,基类的public和protected成员可以直接被访问,就像它们是派生类的成员一样,对多继承这也是正确的。但是在多继承下,派生类可以从两个或者更多个基类中继承同名的成员。然而在这种情况下,直接访问是二义的,将导致编译时刻错误。例如,如果Bear和Endangered都定义了一个成员函数print(),则如下语句:
ying_yang.print( cout );
将导致编译时刻错误,即使这两个通过继承得到的成员函数定义了不同的参数类型:
Error: ying_yang.print( cout ) -- ambiguous, one of
Bear::print( ostream& )
ndangered::print( ostream&, int )
原因在于继承得到的成员函数没有构成派生类中的重载函数,因此,对于print()调用,编译器在解析的时候,只是使用了针对print的名字解析,而不是使用“基于传递给print()的实际实参类型的重载解“。
转换与多个基类
在单继承下,如果有必要的话,派生类的指针或引用可以自动被转换成基类的指针或引用。对于多继承,这也是正确的。例如一个Panda指针或引用可以被转换成ZooAnimal、Bear或Endangered类的指针或引用。例如:
extern void display( const Bear& );
extern void highlight( const Endangered& );
Panda ying_yang;
display( ying_yang ); // ok
highlight( ying_yang ); // ok
extern ostream& operator<
cout << ying_yang << endl; // ok
但是在多继承下二义转换的可能性非常大。例如,考虑下列两个函数:
extern void display( const Bear& );
extern void display( const Endangered&);
用非限定修饰的Panda对象调用display():
Panda ying_yang;
display( ying_yang ); //错误:二义性
将导致下列一般形式的编译时刻错误:
Error: display( ying_yang ) -- ambiguous, one of
extern void display( const Bear&);
extern void display( const Endangered&);
编译器没有办法区分应该使用哪一个直接基类(编译器不会试图根据派生类转换来区别基类间的转换,转换到每个基类都一样好)。
多重继承下的虚函数
为了了解多继承怎样影响虚拟函数机制,让我们为每个Panda的直接基类定义一组虚拟函数:
class Bear : public ZooAnimal {
public:
virtual ~Bear();
virtual ostream& print( ostream&) const;
virtual string isA() const;
// ...
};
class Endangered {
public:
virtual ~Endangered();
virtual ostream& print( ostream&) const;
virtual void highlight() const;
// ...
};
现在我们来定义Panda,它提供了自己的print()实例、析构函数,并引入了一个新的虚拟函数cuddle():
class Panda : public Bear, public Endangered
{
public:
virtual ~Panda();
virtual ostream& print( ostream&) const;
virtual void cuddle();
// ...
};
可以直接从Panda对象调用的虚拟函数集如下表所示:
虚拟函数名
活动实例
Destructor(构析函数)
Panda::~Panda()
print(ostream&) const
Panda::print(ostream&)
isA() const
Bear::isA()
highlight() const
Endangered::highlight()
cuddle()
Panda::cuddle()
当用Panda类对象的地址初始化或赋值Bear或ZooAnimal指针或引用时,Panda接口中“Panda特有的部分“以及”Endangered部分“就都不能再被访问。例如:
Bear *pb = new Panda;
pb->print( cout ); // ok: Panda::print(ostream&)
pb->isA(); // ok: Bear::isA()
pb->cuddle(); //错误:不是Bear接口的部分
pb->highlight(); //错误:不是Bear接口的部分
delete pb; // ok: Panda::~Panda()
类似地,当用Panda类对象的地址初始化或赋值Endangered指针或引用时,Panda接口中“Panda特有的部分“以及”Bear部分“都不能再被访问。例如:
Endangered *pe = new Panda;
pe->print( cout ); // ok: Panda::print(ostream&)
pe->isA(); //错误:不是Endangered的接口部分
pe->cuddle(); //错误:不是Endangered的接口部分
pe->highlight(); // ok: Endangered::highlight()
delete pe; // ok: Panda::~Panda()
无论我们删除对象所使用的指针类型是什么,虚拟析构函数的处理都是一致的。例如:
// ZooAnimal *pz = new Panda;
delete pz;
// Bear *pb = new Panda;
delete pb;
// Panda *pp = new Panda;
delete pp;
// Endangered *pe = new Panda;
delete pe;
在上述例子中,析构函数的调用顺序完全相同。析构函数调用顺序与构造函数的顺序相反:Panda析构函数被通过虚拟机制调用。在Panda析构函数执行之后依次静态调用Endangered、Bear和ZooAnimal析构函数。