跳至主要內容

菱形继承问题

张威大约 4 分钟c/c++继承

菱形继承问题

多重继承:可以复用多个基类的代码到派生类中。

但是多重继承中也会出现问题:我们来看一个经典的问题:

A为B、C的基类,B从A单继承而来,C从A也是单继承而来;D是B和C多继承而来,D有两个基类分别为B和C。A称为D的间接基类,D也有A的数据。
   假设A中有ma变量,B从A继承来并且有自己的mb,C从A继承来并且有自己的mc,D从B与C多继承而来,从B继承来了ma与mb,从C继承来了ma与mc,D也有自己的属性md;那么就出现了问题,<font color='red'>我们的**间接基类D有多份ma属性**,这就是菱形继承问题</font>。

当然,我们多重继承还会出现别的问题: 半圆形继承问题: B从A单一继承而来,C有一个基类B而且同时还从A继承而来。A到B为单继承,C为多继承。 假设ma为类A的属性,B从A继承而来,B有自己的mb;C从B与A继承而来,C有自己的mc,又从A继承来了ma,从B继承来了mb与mc;产生了菱形继承同样的问题,C中出现了两个ma属性。

我们用代码来实现一下菱形继承问题:

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对象的内存布局。

D能看见B,C与md,所以D在构造时调用B,C的构造及ma的初始化。ma的初始化在B与C的构造函数中进行,因此D内存为20个字节。打印一下:

,最后是D的构造;析构顺序与其相反。我们发现,D这个派生类中调用了两次A的构造,

那么如何处理这种问题呢?就需要虚继承来处理了。所有从A继承而来的地方都采用虚继承,A就为虚基类。 此时:。虚继承就可以解决多重继承中的菱形继承与半圆形继承出现的问题了。

代码修改为如下:

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:
	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;
}

打印结果:修改成功;A、B、C、D各初始化与析构一次。