相关内容:
C++
允许一个派生类从多个基类继承,这种继承方式称为多重继承,当从多个基类继承时每个基类之间用逗号隔开,比如class A:public B, public C{}
就表示派生类A
从基类B
和C
继承而来。
多重继承的构造函数和析构函数:多重继承中初始化的次序是按继承的次序来调用构造函数的而不是按初始化列表的次序, 比如有class A:public B, public C{}
那么在定义类A
的对象A m
时将首先由类A
的构造函数调用类B
的构造函数初始化B
,然后调用类C
的构造函数初始化C
,最后再初始化对象A
,这与在类A
中的初始化列表次序无关。
多重继承中的二义性问题:
第1种二义性问题:成员名重复:比如类A
从类B
和C
继承而来,而类B
和C
中都包含有一个名字为f的成员函数,这时派生类A
创建一个对象,比如A m;
语句m.f()
将调用类B
中的f
函数呢还是类C
中的f函数呢?
第2种二义性问题(菱形继承):多个基类副本:比如类C
和B
都从类D
继承而来,这时class A:public B, public C{}
类A
从类C
和类B
同时继
承而来,这时类A
中就有两个类D
的副本,一个是由类B
继承而来的,一个是由类C
继承而来的,当类A
的
对象比如A m;
要访问类D
中的成员函数f时,语句m.f()
就会出现二义性,这个f
函数是调用的类B
继承来的f
还是访问类C
继承来的函数f
呢?
在第2种情况下还有种情况,语句
classA:public B,public C{}
,因为类A
首先使用类B
的构造函数调用共同基类D
的构造函数构造第一个类D
的副本,然后再使用类C
的构造函数调用共同基类D
的构造函数构造第二个类D
的副本。类A
的对象m
总共有两个共享基类D
的副本,这时如果类D
中有一个公共成员变量d
,则语句m.B::d
和m.D::d
都是访问的同一变量,类B
和类D
都共享同一个副本,既如果有语句m.D::d=3
则m.B::d
也
将是3
。这时m.C::d
的值不受影响而是原来的值。为什么会这样呢?
因为类
A
的对象m
总共只有两个类D
的副本,所以类A
的对象m
就会从A
继承来的两个直接基类B
和C
中,把从共同基类D
中最先构造的第一个副本作为类A
的副本, 即类B
构造的D
的副本。 因为class A:public B,public C{}
最先使用B
的构造函数调用共同基类类D
创造D
的第一个副本,所以类B
和类D
共享同一个副本。
解决方法:对于第1 和第 2 种情况都可以使用作用域运算符::
来限定要访问的类名来解决二义性。但对于第二种情况一般不允许出现两个基类的副本,这时可以使用虚基类来解决这个问题 ,一旦定义了虚基类,就只会有一个基类的副本 。
虚基类:
虚基类:方法是使用virtual
关见字,比如class B:public virtual D{}
,class C:virtual public D{}
注意关键字virtual
的次
序不关紧要。类B
和类C
以虚基类的方式从类D
继承,这样的话从类B
和类C
同时继承的类时就会只创建一个类D
的副本,比如class A:public B, public C{}
这时类A
的对象就只会有一个类D
的副本,类A
类B
类C
类D
四个类都共享一个类D
的副本,比如类D
有一个公有成员变量d
,则m.d
和 m.A::d
,m.B::d
,m.C::d
,m.D::d
都将访问的
是同一个变量。这样类A的对象调用类D
中的成员时就不会出 现二义性了 。
虚基类的构造函数:比如class B:public virtual D{}
;class C:virtual public D{}
; class A:public B,public C{}
;这时当创
建类A
的对象A m
时初始化虚基类D
将会使用类A
的构造函数直接调用虚基类的构造函数初始化虚基类部分,而
不会使用类B
或者类C
的构造函数调用虚基类的构造函数初始化虚基类部分, 这样就保证了只有一个虚基类的副本。
但是当创建一个类B
和类C
的对象时仍然会使用类B
和类C
中的构造函数调用虚基类的构造函数初始化虚基类。
代码相关内容:
1 |
|
代码执行结果:
(普通)二义性问题:
1 |
|
代码执行结果:
菱形二义性问题:
1 |
|