一 概述:
(1)多重继承:从多余一个直接基类派生类的能力,多重继承的派生类继承所有父类的属性。
二 多重继承对象的构造(单继承一样):
(1)构造函数位置:与单继承一样,在派生类构造函数的初始化列表式中给多个基类的构造函数传递值。
(2)构造次序:基类构造函数按照基类在“类派生列表中出现的次序”调用。派生类只能初始化直接基类(virtual继承除外)。
*例如:class derived:public base2,public base3{},其中class base2:public base1。
*则derived类的构造函数的次序为:base1、base2、base3。
(3)析构次序:与构造次序相反,上例析构顺序为:base3、base2、base1。
(4)析构函数特点:派生类析构函数不负责撤销基类对象的成员。编译器总是显示调用派生类对象基类部分的析构函数。每个析构函数只负责清除自己的成员。
三 多重继承派生类的复制控制(单继承一样):
(1)多重继承派生类的逐个成员初始化、赋值和析构与单继承一样:使用基类自己的复制构造函数、赋值操作符或析构函数构造、赋值或撤销每个基类。
*如果派生类显式定义了自己的赋值构造函数或赋值操作符,则该定义将完全覆盖默认定义。继承类的赋值构造函数和赋值操作符负责对基类成分以及自己的成员进行复制或赋值。
*如果派生类定义了自己的复制构造函数,该复制构造函数一般应显式使用基类赋值构造函数初始化对象的基类部分(在初始化列表中)。如:derived(const derived &d):base(d){/*............*/}
*如果没有显式调用基类复制构造函数,则会运行base的默认构造函数初始对象的基类部分,造成不一致:基类部分保持默认值,而derived部分是另一个对象的副本。
(2)赋值操作符:如果派生类定义了自己的赋值操作符,则操作符必须对基类部分进行显式赋值(在派生类赋值操作符重载定义函数内部调用基类重载的赋值操作符)。
*如果没有显示调用基类的赋值操作符,将造成不一致(d1=d2):d1的基类部分还是自己的,但是派生类部分是d2的。
四 多重继承下的类作用域:
(1)名字查找规则(成员函数中使用的名字和查找步骤):
*首先,在函数本身进行;
*其次,在成员所在类中;
*然后,依次查找每个基类,在多重继承下同时检查所有基类继承。如果在多个基类中找到该名字,则那个名字的使用必须显式指定使用哪个基类;否则,该名字的使用是二义性的。
(2)名字查找两个步骤:
1.首先,编译器找到一个匹配的声明(找到两个以上则导致二义性)。
2.然后,编译器才确定找到的声明是否合法。
3.所以:及时继承的两个函数由不同的形参表;或者在一个基类中是私有,而另一个类中是共有或受保护的,都会导致二义性。
4.如果名字属于同一个虚基类成员,则没有二义性。
5.如果某个派生路径中x是虚基类成员,而另一个派生路径中x是后代派生类成员,也无二义性。特定派生类实例的优先级高于共享虚基类实例。
五 虚继承:
(1)虚基类的声明:
*通过关键字virtual声明,将基类指定为通过虚继承派生。
(2)虚基类的初始化:
*在虚派生中,由最底层派生类的构造函数初始化虚基类,避免了常规规则(派生类只能初始化直接基类)造成的多次初始化虚基类。
(3)构造虚基类对象:(例如:base是虚基类,base1和base2继承base,derived继承base1和base2)
1.创建derived对象的步骤:
*首先是要构造函数初始化列表中指定的初始化式构造虚基类base。
*接下来,按照derived类的“类派生列表”中声明的次序构造依次base1或base2,同时忽略base1和base2中用于base构造函数初始化列表的初始化式。
*最后,构造derived部分。
2.复制构造函数和赋值操作符使用同样的次序。
3.析构函数与构造的次序相反。