C++多重继承 - 菱形继承问题
B和C的基类是A,B和C都从A单继承而来,D是从B和C多继承而来,D有2个基类:B和C
我们把A称为D的间接的基类
假设在A里面有ma这个成员变量
很明显,我们可以看到问题,这里的派生类对象D有间接基类A多份的数据,这在我们的软件设计上肯定是有问题的,假设ma表示名字,我们不可能在派生类D上有2个名字,或者说ma代表身份id,在派生类D不可能有2个身份id,这是设计上的问题。
不仅仅有菱形继承,还有这种继承:
B从A单一继承而来,C有一个基类B,但是还同时从A继承而来。
假设ma是类A的一个属性。
很明显,出现了同样的问题。
C不可能有2个相同意义的属性。
多重继承的弊端:派生类有多份间接基类的数据 设计的问题
好处是可以做更多代码的复用
在设计软件项目,如果可以,我们要尽量避免多重继承的情况,避免出现以上问题。
菱形继承的案例
class A
{
public:
A(int data) :ma(data) { cout << "A()" << endl; }
~A() { cout << "~A()" << endl; }
protected:
int ma;
};
//=======================================
class B : public A
{
public:
B(int data) :A(data), mb(data) { cout << "B()" << endl; }
~B() { cout << "~B()" << endl; }
protected:
int mb;
};
class C : public A
{
public:
C(int data) :A(data), mc(data) { cout << "C()" << endl; }
~C() { cout << "~C()" << endl; }
protected:
int mc;
};
//=========================================
class D : public B, public C
{
public:
D(int data) : B(data), C(data), md(data) { cout << "D()" << endl; }
~D() { cout << "~D()" << endl; }
protected:
int md;
};
int main()
{
D d(10);
return 0;
}
d对象内存的布局:
两个ma分别在B和C的构造函数里面进行
D调用了2次A的构造
不管是内存占用上,还是数据的重复,我们的软件设计是要杜绝的。
菱形问题怎么处理呢
虚继承。
所有从A继承的地方,都采用virtual虚继承,
这样,A就成了虚基类了。
B从A虚继承,A成为虚基类,就要搬到派生类的最后面。
C也从A虚继承而来。也要把ma搬到最后面。搬的时候,发现最后面已经有一份虚基类的数据了,那么当前这份虚基类数据就不要了,同样的放一个vbptr
现在在派生类D里面,现在只有1份基类的数据。
不管访问B下的ma,还是访问C下的ma,都是同一个ma
B和C都要采用虚继承哦。
如果其中一个没有采用虚继承,ma还是会重复。
也就是说,所有从A继承的地方都要使用虚继承。
最终,在派生类的最后面只会放1份虚基类的数据。
但是,我们把虚基类的数据放到了派生类的最后面来。
也就是说,D给B,C说:你们不要每个人都拿1份ma了,这个ma直接给我,我用1份,你们要访问,都跑到我这个ma来,这样一来,ma的初始化从作用域上来说,已经靠近D了,是属于D上的,不需要B或者C来负责初始化了,ma这个虚基类数据需要由D自己来初始化。
class A
{
public:
A(int data) :ma(data) { cout << "A()" << endl; }
~A() { cout << "~A()" << endl; }
protected:
int ma;
};
//=======================================
class B : virtual public A
{
public:
B(int data) :A(data), mb(data) { cout << "B()" << endl; }
~B() { cout << "~B()" << endl; }
protected:
int mb;
};
class C : virtual public A
{
public:
C(int data) :A(data), mc(data) { cout << "C()" << endl; }
~C() { cout << "~C()" << endl; }
protected:
int mc;
};
//=========================================
class D : public B, public C
{
public:
//“A::A”: 没有合适的默认构造函数可用
D(int data) :A(data), B(data), C(data), md(data) { cout << "D()" << endl; }
~D() { cout << "~D()" << endl; }
protected:
int md;
};
int main()
{
D d(10);
return 0;
}
菱形继承问题解决了!