说说C++多重继承(一)

2015-07-20 17:08:13 · 作者: · 浏览: 11
尽管大多数应用程序都使用单个基类的公用继承,但有些时候单继承是不够用的,因为可能无法为问题域建模或对模型带来不必要的复杂性。在这种情况下,多重继承可以更直接地为应用程序建模。
?
一、基本概念
?
多重继承是从多于一个直接基类派生类的能力,多重继承的派生类继承其父类的属性。
?
?
class ZooAnimal{
};
class Bear : public ZooAnimal{
};
class Endangered{
};
class Panda : public Bear, public Endangered{
};
?
注意:
?
(1)与单继承一样,只有在定义之后,类才可以用作多重继承的基类。
?
(2)对于类可以继承的基类的数目,没有语言强加的限制,但在一个给定派生列表中,一个基类只能出现一次。
?
?
?
1、多重继承的派生类从每个基类中继承状态
?
Panda ying_yang("ying_yang");
对象ying_yang包含一个Bear子类对象、一个Endangered子类对象以及Panda类中声明的非static数据成员。如下图所示:
?
?
?
?
?
2、派生类构造函数初始化所有基类
?
派生类构造函数可以早构造函数初始化式中给零个或多个基类传递值。
?
Panda::Panda(string name, bool onExhibit)
? ? : Bear(name, onExhibit, "Panda"),
? ? Endangered(Endangered::critical){}
构造函数初始化式只能控制用于初始化基类的值,不能控制基类的构造次序。基类构造函数按照基类构造函数在类派生列表中的出现次序调用。对于Panda,基类初始化次序是:
?
(1)ZooAnimal。
?
(2)Bear,第一个直接基类。
?
(3)Endangered,第二个直接基类,它本身没有基类。
?
(4)Panda,初始化本身成员,然后运行它的构造函数的函数体。
?
注意:构造函数调用次序既不受构造函数初始化列表中出现的基类的影响,也不受基类在构造函数初始化列表中的出现次序的影响。例如:
?
Panda::Panda() : Endangered(Endangered::critical){}
这个构造函数将隐式调用Bear的默认构造函数,尽管它不出现在构造函数初始化列表中,但仍然在Endangered类构造函数之前调用。
?
3、析构的次序
?
按照构造函数运行的逆序调用析构函数。Panda、Endangered、Bear,ZooAnimal。
?
?
?
二、转换与多个基类
?
单个基类情况下,派生类的指针或引用可自动转换为基类的指针或引用。对于多重继承,派生类的指针或引用可以转换为其任意基类的指针或引用。
?
注意:在多重继承情况下,遇到二义性转换的可能性更大。编译器不会试图根据派生类转换来区别基类间的转换,转换到每个基类都一样好。例如:
?
void print(const Bear&);
void print(const Endangered&);
通过Panda对象调用print时,会导致一个编译时错误。
?
1、基于指针或引用类型的查找
?
与单继承一样,用基类的指针或引用只能访问基类中定义(或继承)的成员,不能访问派生类中引入的成员。当一个类派生于多个基类的时候,那些基类之间没有隐含的关系,不允许使用一个基类的指针访问其他基类的成员。例如:
?
?
class ZooAnimal
{
public:
? ? virtual void print(){}
? ? virtual ~ZooAnimal(){}
};
class Bear : public ZooAnimal
{
public:
? ? virtual void print()
? ? {
? ? ? ? cout << "I am Bear" << endl;
? ? }
? ? virtual void toes(){}
};
class Endangered
{
public:
? ? virtual void print(){}
? ? virtual void highlight()
? ? {
? ? ? ? cout << "I am Endangered.highlight" << endl;
? ? }
? ? virtual ~Endangered(){}
};
class Panda : public Bear, public Endangered
{
public:
? ? virtual void print()
? ? {
? ? ? ? cout << "I am Panda" << endl;
? ? }
? ? virtual void highlight()
? ? {
? ? ? ? cout << "I am Panda.highlight" << endl;
? ? }
? ? virtual void toes(){}
? ? virtual void cuddle(){}
? ? virtual ~Panda()
? ? {
? ? ? ? cout << "Goodby Panda" << endl;
? ? }
};
?
当有如下调用发生时:
?
?
int main()
{
? ? Bear *pb = new Panda();
? ? pb->print(); ? ? ? ? ? ?//ok: Panda::print
// ? ?pb->cuddle(); ? ? ? ? ? ?//error: not part of Bear interface
// ? ?pb->highlight(); ? ? ? ?//error: not part of Bear interface
? ? delete pb; ? ? ? ? ? ? ? ?//Panda::~Panda
?
? ? Endangered *pe = new Panda();
? ? pe->print(); ? ? ? ? ? ?//ok: Panda::print
// ? ?pe->toes(); ? ? ? ? ? ? ? ?//error: not part of Endangered interface
// ? ?pe->cuddle(); ? ? ? ? ? ?//error: not part of Endangered interface
? ? pe->highlight(); ? ? ? ?//ok: Panda::highlight
? ? delete pe; ? ? ? ? ? ? ? ?//Panda::~Panda
?
? ? return 0;
}
?
?
?
2、确定使用哪个虚析构函数
?
我们假定所有根基类都将它们的析构函数定义为虚函数,那么通过下面几种删除指针方法,虚析构函数处理都是一致的。
?
delete pz; ? ? ? ? ? ?//pz is a ZooAnimal*
delete pb; ? ? ? ? ? ?//pb is a Bear*
delete pp; ? ? ? ? ? ?//pp is a Panda*
delete pe; ? ? ? ? ? ?//pe is a Endangered*
假定上面四个指针都指向Panda对象,则每种情况发生完全相同的析构函数调用次序,