C++ Primer 有感(多重继承与虚继承)
1.多重继承的构造次序:基类构造函数按照基类构造函数在类派生列表中的出现次序调用,构造函数调用次序既不受构造函数初始化列表中出现的基类的影响,也不受基类在构造函数初始化列表中的出现次序的影响。
2.在单个基类情况下,派生类的指针或引用可以自动转换为基类的指针或引用,对于多重继承也是如此,派生类的指针或引用可以转换为其任意基类的指针或引用。
3.多重继承像单继承一样,用基类的指针或引用只能访问基类中定义(或继承)的成员,不能访问派生类中引入的成员。
当一个类继承于多个基类的时候,那些基类之间没有隐含的关系,不允许使用一个基类的指针访问其他基类的成员。
4.如果用户自己定义了派生类自己的复制构造函数或赋值操作符,则用户要负责复制(赋值)所有的基类子部分。而对于编译器默认合成的派生类的复制构造函数或赋值操作符会自动复制或赋值基类部分。
5.在多重继承下,通常成员函数中使用的名字的查找首先在函数本身进行,如果不能在本地找到名字,就继续在成员的类中查找,然后依次查找每个基类。注意名字查找总是按照两个步骤进行的:(1)首先编译器找到一个匹配的声明(与参数、返回值、公有私有均没有关系,只与名字有关) (2)然后,编译器才确定所找到的声明是否合法。
6.虚继承是一种机制,类通过虚继承指出它希望共享其虚基类的状态。在虚继承下,对给定虚基类,无论该类在派生类层次中作为虚基类出现多少次,只继承一个共享的基类子对象。共享的基类子对象称为虚基类。
例如:
class istream: public virtual ios{ /*....*/ }; class ostream: virtual public ios{ /*....*/ };
7.通过用关键字virtual修改声明,将基类指定为通过虚继承派生:
class istream: public virtual ios{ /*....*/ }; class ostream: virtual public ios{ /*....*/ };指定虚派生只影响从指定了虚基类的类派生的类。除了影响派生类自己的之外,它也是关于派生类与自己的未来派生类的关系的一个陈述。virtual说明符陈述了在后代派生类中共享指定基类的单个实例的愿望。
8.每个类只初始化自己的直接类。在应用于虚基类的时候,这个初始化策略会失败。如果使用常规规则,就可能会多次初始化虚基类。为了解决这个重复初始化问题,从具有虚基类的类继承的类对初始化进行特殊处理。在虚派生中,由最低层派生类的构造函数初始化虚基类。
虽然由最低层派生类初始化虚基类,但是任何直接或间接继承虚基类的类一般也必须为该基类提供自己的初始化式。只要可以创建虚基类派生类类型的独立对象,该类就必须初始化自己的虚基类,这些初始化式只在创建中间类型的对象时使用。
例如:
Bear::Bear(std::string name,bool onExhibit):ZooAnimal(name,onExhibit,"Bear") { } Raccoon::Raccoon(std::string name,bool onExhibit):ZooAnimal(name,onExhibit,"Raccoon") { }虽然ZooAnimal不是Panda的直接基类,但是Panda构造函数也初始化ZooAnimal基类:
Panda::Panda(std::string name,bool onExhibit):ZooAnimal(name,onExhibit,"Panda"),Bear(name,onExhibit),Raccoon(name,onExhibit),Endangered(Endangered::critical),sleeping_flag(false) { }当创建Panda对象的时候:
(1)首先使用构造函数初始化列表中指定的初始化式构造ZooAnimal部分。
(2)接下来,构造Bear部分。忽略Bear的用于ZooAnimal构造函数初始化列表的初始化式。
(3)然后,构造Raccoon部分。再次忽略ZooAnimal初始化式。
(4)最后,构造Panda部分。
如果Panda构造函数不显式初始化ZooAnimal基类,就使用ZooAnimal默认构造函数;如果ZooAnimal没有默认构造函数,则代码出错。
9.无论虚基类出现在继承层次中任何地方,总是在构造非基类之前构造虚基类:
class Character{ /*....*/ }; class BookCharacter: public Character{ /*....*/ }; class ToyAnimal{ /*...*/ }; class TeddyBear: public BookChatacter,public Bear,public virtual ToyAnimal{ /*...*/ };
按声明次序检查直接基类,确定是否在虚基类。例中,首先检查BookChatacter的继承子树,然后检查Bear的继承子树,最后检查ToyAnimal的继承子树。按从根类开始向下倒最低层派生类的次序检查每个子树。
TeddyBear的虚基类的构造次序是先ZooAnimal再ToyAnimal。一旦构造了虚基类,就按声明次序调用非虚基类的构造函数:首先是BookChatacter,它导致调用Character的构造函数,然后是Bear。因此,为了创建TeddyBear对象,按下面次序调用构造函数:
ZooAnimal(); ToyAnimal(); Character(); BookChatacter(); Bear(); TeddyBear();在这里,由最低层派生类TeddyBear指定用于ZooAnimal和ToyAnimal的初始化式。
在合成复制构造函数中使用同样的构造次序,在合成赋值操作符中也是按这个次序给基类赋值。保证调用基类的析构函数的次序与构造函数的调用次序相反。