高质量程序设计指南c++/c语言(34)--多重继承
多重继承的继承列表中,一个基类只能出现一次。先看一个例子:
#include<iostream> using namespace std; class ZooAnimal { public: ZooAnimal() { cout << "ZooAnimal" << endl; } ZooAnimal(int data) { cout << "ZooAnimal(int data)" << endl; d1 = data; } private: int d1; }; class Bear: public ZooAnimal { public: Bear() { cout << "Bear" << endl; } Bear(int data) { cout << "Bear(int data)" << endl; d2 = data; } private: int d2; }; class Endangered { public: Endangered() { cout << "Endangered" << endl; } Endangered(int data) { cout << "Endangered(int data)" << endl; d3 = data; } private: int d3; }; class Panda: public Bear, public Endangered { public: Panda() { cout << "Panda" << endl; } Panda(int data):Endangered(data), Bear(data) { cout << "Panda(int data)" << endl; d4 = data; } private: int d4; }; int main(void) { Panda panda(10); return 0; }
1、派生类构造函数初始化所有基类
// explicitly initialize both base classes Panda::Panda(int data):Bear(data), Endangered(data){} // implicitly use Bear default constructor to initialize base Bear Panda::Panda(int data):Endangered(data){}
2、构造的次序
构造函数的初始化列表只能控制用于初始化基类的值,不能控制基类的构造次序。基类的构造函数是按照基类的继承顺序被调用的
class Panda: public Bear, public Endangered,即对Panda而言,基类初始化的顺序:
(1)ZooAnimal
(2)Bear
(3)Endangered
(4)Panda
3、析构的次序
总是按照构造函数运行的逆序调用析构函数,即~Panda(),~Endangered(),~Bear(),~ZooAnimal()
4、多重继承下的类作用域
在多重继承下,成员函数中使用的名字的查找首先在函数本身进行,如果不能在本地找到名字,就继续在成员的类中查找,然后依次查找每个基类。在多重继承下,查找同时检查所有的基类继承子树---在我们的例子中,并行查找Endangered子树和Bear/ZooAnimal子树。如果在多个子树中找到改名字,则那个名字的使用必须显示的指定使用哪个基类;否则,该名字的使用是二义性的。
假定Bear类和Endangered类都定义了一个print函数,而Panda类没有定义该函数,则 Panda panda; panda.print();这样的语句将导致编译时错误。Panda类有两个print函数是完全合法的。派生只是导致潜在的二义性,如果没有Panda对象调用print,那就可以避免二义性。如果每个print调用明确指出想要哪个版本--Bear::print还是Endangered::print,也可以避免错误。只有在存在使用该成员的二义性尝试的时候,才会出错。
函数调用时首先发生名字查找。虽然两个继承的print成员的二义性相当明显,但是也许更令人惊讶的是,即使两个继承的函数有不同的形参表,也会产生错误。类似的,即使函数在一个类中是私有的而在另一个类中是公用的或受保护的,也是错误的。最后,如果在ZooAnimal类中定义了print而在Bear类中没有定义,调用仍是错误的。