多继承和菱形继承

一、多继承

多继承:一个子类有多个父类称为多继承。

多继承存在问题导致二义性:派生类访问基类成员出现的不确定性。

class B1
{
public:
	void SetValue()
	{
		cout << "B1::SetValue" << endl;
	}
	int _b1;
	int _c;
};

class B2
{
public:
	void SetValue()
	{
		cout << "B2::SetValue" << endl;
	}
	int _b2;
	int _c;
};

class D :public B1, public B2
{
public:
	int _d;
};

int main()
{
	D d;
	//d.SetValue();
	//d._c = 10;//因为D继承了B1和B2,并且两个基类中都包含_c成员,所以访问不确定。同理函数也是一样
	//解决上述问题,需要显示调用
	d.B1::SetValue();
	d.B2::_c = 10;
	return 0;
}

多继承对象模型特点:基类在上,派生类在下,谁先继承谁在上。

 

二、虚拟继承

在多继承中有一种情况如下图继承关系:菱形继承

菱形继承缺点导致在D中访问B成员造成的数据二义性,和对B中数据存储两份导致数据冗余问题。

解决菱形继承首先我们了解虚拟继承。

虚拟继承:在继承的同时添加virtual关键字

至此,已经知道了虚拟继承对象模型布局,那么派生类部分的前四个字节到底是什么呢?

派生类对象前四个字节就是偏移量表格的地址(我们称该地址为虚基表指针,偏移量表格称为虚基表

 

单继承和虚拟继承对象模型区别:

  • 对象模型中派生类和基类位置相反。单继承:基类在上,派生类在下。虚拟继承:基类在下,派生类在上。
  • 虚拟继承多了四个字节,用于存储虚基表的地址。存储在派生类部分的前四个字节。并且虚基表指针实在创建派生类对象时从构造函数填入。
  • 对基类成员访问方式不同。普通单继承是直接访问,虚拟继承需要经过三步才可以访问。
  • 虚拟继承如果用户没有显示给出构造函数,编译器会默认生成一个派生类构造函数。而普通单继承如果没有必要,编译器也不会生成。

虚拟继承中默认生成的构造函数作用?

构造函数通过参数控制将偏移量表格(虚基表指针)填入到虚拟继承派生类对象模型前四个字节中。该参数控制:如果是虚拟继承,那么会push  1;否则,不是虚拟继承。所以可见  1 是虚拟继承的标志。

三、菱形虚拟继承

class B{
public:
	int _b;
};

class C1 :virtual public B
{
public:
	int _c1;
};

class C2 :virtual public B
{
public:
	int _c2;
};

class D :public C1, public C2
{
public:
	int _d;
};

int main()
{
	D d;
	d._b = 1;
	d._c1 = 2;
	d._c2 = 3;
	d._d = 5;
	return 0;
}

从上述对象模型可知,菱形虚拟继承解决了菱形继承的二义性和数据冗余。

猜你喜欢

转载自blog.csdn.net/weixin_41318405/article/details/88063902