C++ 多重继承与虚基类

单一继承是指:一个派生类只继承一个基类。

多重继承指的是一个类可以同时继承多个不同基类的行为和特征功能

class Base1
{
public:
	Base1() { cout << "Base1()" << endl; }
	~Base1() { cout << "~Base1()" << endl; }
};
class Base2
{
public:
	Base2() { cout << "Base2()" << endl; }
	~Base2() { cout << "~Base2()" << endl; }
};

/*
**  : 之后称为类派生表,表的顺序决定基类构造函数
** 调用的顺序,析构函数的调用顺序正好相反
*/
class Derive : public Base2, public Base1
{
};
int main()
{
	Derive derive;
	return 0;
}

多重继承有什么好处?坏处?怎么解决?

优点:多重继承与单一继承一样,都可以是的代码得到更好的复用,思路清晰,代码简单。

缺点:1.二义性问题

           2.菱形继承导致派生类持有间接基类的多份拷贝

菱形继承导致派生类持有间接基类的多份拷贝

class A
{
public:
	A(int data = 0) :ma(data) { cout << "A" << endl; }
	~A() { cout <<" ~A()" << endl; }

private:
	int ma;
};
class B : public A
{
public:
	B(int data = 0) :A(data),mb(data) { cout << "B" << endl; }
	~B() { cout << " ~B()" << endl; }
private:
	int mb;
};
class C: public A
{
public:
	C(int data = 0) :A(data),mc(data) { cout << "C" << endl; }
	~C() { cout << " ~C()" << endl; }

private:
	int mc;

};
class D :public B, public C
{
public:
	D(int data = 0) :B(data),C(data), md(data) { cout << "D" << endl; }
	~D() { cout << " ~D()" << endl; }

private:
	int md;
};
int main()
{
	D d;
	return 0;
}

这样会使的D含有两份相同的A的成员变量 。 

二义性问题 

此时我们不知道调用B与C中的那个show()函数。

如何解决这样的问题? 需要用到我们的虚基类

虚基类

首先需要先分清楚虚继承与虚函数

虚继承是解决C++多重继承问题的一种手段,从不同途径继承来的同一基类,会在派生类中存在多份拷贝。

这将存在两个问题:1.浪费存储空间
                                2.存在二义性问题。

通常可以将派生类对象的地址赋值给基类对象,实现的具体方式是,将基类指针指向继承类(继承类有基类的拷贝)中的基类对象的地址,但是多重继承可能存在一个基类的多份拷贝,这就出现了二义性。

虚基类使得从多个类(它们的基类相同)派生出的对象只继承一个基类对象。例如,通过在类声明中使用关键字 virtual ,可以使这些派生类只保留虚基类的一个副本。
 

class A//由于B与C虚继承A,此时A就是一个虚基类
{
public:
	A(int data = 0) :ma(data) { cout << "A" << endl; }
	~A() { cout <<" ~A()" << endl; }

private:
	int ma;
};
class B :virtual public A
{
public:
	B(int data = 0) :A(data),mb(data) { cout << "B" << endl; }
	~B() { cout << " ~B()" << endl; }
private:
	int mb;
};
class C:virtual public A
{
public:
	C(int data = 0) :A(data),mc(data) { cout << "C" << endl; }
	~C() { cout << " ~C()" << endl; }

private:
	int mc;

};
class D :public B, public C
{
public:
	D(int data = 0) :B(data),C(data), md(data) { cout << "D" << endl; }
	~D() { cout << " ~D()" << endl; }

private:
	int md;
};
int main()
{
	D d;
	return 0;
}

D中此时只有一份A的成员变量 

Derive1 和 Derive2 虚继承了 Base后, Base 成为了Derive1 和 Derive2 的虚基类,那 Derive3 就可以安全的多继承 Derive1 和 Derive2了。如下图:

虚继承底层的内存布局


虚基类的实现是产生虚基类表指针 vbptr 与虚基类表 vbtable。

虚继承底层实现原理与编译器相关,一般通过虚基类指针和虚基类表实现,每个虚继承的子类都有一个虚基类指针(占用一个指针的存储空间,4字节)和虚基类表(不占用类对象的存储空间)。

需要强调的是,虚基类依旧会在派生类里面存在拷贝,只是仅仅只存在一份而已,并不是不在派生类里面了;当虚继承的派生类被当做基类继承时,虚基类指针也会被继承。

实际上,vbptr指的是虚基类表指针(virtual base table pointer),该指针指向了一个虚基类表(virtual table),虚基类表中记录了虚基类与本类的偏移地址;

通过偏移地址,这样就找到了虚基类成员,而虚继承也不用像普通多继承那样维持着公共基类(虚基类)的两份同样的拷贝,节省了存储空间。

在这里我们可以对比虚函数的实现原理:他们有相似之处,都利用了虚指针(均占用对象的存储空间)和虚表(均不占用对象的存储空间)。

虚基类依旧存在继承类中,只占用存储空间;虚函数不占用存储空间。
虚基类表存储的是虚基类相对直接继承类的偏移;而虚函数表存储的是虚函数地址。
接下来我们从内存布局看一下具体过程:

 

class A
{
public:
	A(int data = 0) :ma(data) { cout << "A()" << endl; }
	~A() { cout << " ~A()" << endl; }

private:
	int ma;
};
class B :virtual public A
{
public:
	B(int data = 0) :A(data), mb(data) { cout << "B()" << endl; }
	~B() { cout << " ~B()" << endl; }
private:
	int mb;
};
class C :virtual public A
{
public:
	C(int data = 0) :A(data), mc(data) { cout << "C()" << endl; }
	~C() { cout << " ~C()" << endl; }

private:
	int mc;

};
class D :public B, public C
{
public:
	D(int data = 0) :B(data), C(data), md(data) { cout << "D()" << endl; }
	~D() { cout << " ~D()" << endl; }

private:
	int md;
};

猜你喜欢

转载自blog.csdn.net/zhangfei5354/article/details/89470662