什么是虚继承
C++支持多继承的形式,但若被继承的不同基类又同属与某一个接口类or基类的派生类,那么很容易产生经典的菱形继承问题,这样不可避免的带来了命名冲突,访问不明确的问题,如图所示:
虚继承解决的问题菱形继承是什么?
- 基类A派生出类B和类C
- 派生类D同时继承自类B和类C,这时候基类A中的成员变量、成员函数在类D中变成了两份
- 路径1:A–>B–>D;路径2:A–>C–>D
派生类中保留间接基类的多份成员,保留的成员分别存储在不同的直接基类下,虽然可以通过类+作用域的形式去访问修改,但这很容易产生bug,大多数情况下都是冗余的。
代码释义及原理分析
//base基类A
class A {
protected:
int m_a;
};
//派生基类B
class B : public A {
protected:
int m_b;
};
//派生基类C
class C : public A {
protected:
int m_c;
};
//派生类D
class D : public B, public C {
public:
void Set_a(int a) {
m_a = a; //访问不明确
C::m_a = a; //C中的a
B::m_a = a; //B 中的a
}
private:
int m_d;
};
这个问题在编译期就会报错,告知访问不明确,从D的内存布局上们也可以清楚的看到m_a变量会有两份,分别在Base Class B和Base Class C下面
虚继承
//base基类A
class A {
protected:
int m_a;
};
//派生基类B
class B : virtual public A {
protected:
int m_b;
};
//派生基类C
class C : virtual public A {
protected:
int m_c;
};
//派生类D
class D : public B, public C {
public:
void Set_a(int a) {
m_a = a; //虚基类中的a
C::m_a = a; //虚基类中的a
B::m_a = a; //虚基类中的a
}
private:
int m_d;
};
只需要我们基类B和C的定义时,通过virtual public A 的方式进行虚继承,这样在内存布局上,类B或者C的内存布局就会添加一个虚基类指针,并指向虚基类表。
另外我们也可以通过编译器直接查看内存布局,发现和我们的分析完全一致,这样当我们需要访问m_a时都会最终通过虚基类表,找到唯一的一份变量。
虚继承的常见问题
-
间接基类没有虚继承,直接基类进行虚继承,最终仍会保留多份间接基类
-
一个直接基类通过虚继承派生,另一个直接基类普通继承,也会保留两份间接基类,一份在直接继承的类中,另一份在虚基类中