C++中的虚继承
在 C++ 里,虚继承是一种特殊的继承方式,其主要作用是解决多重继承时出现的菱形(diamond问题)继承问题,防止数据冗余和歧义。
通过一个例子来说明:
#include <iostream>
using namespace std;class A {
public:int data;
};class B : public A {};
class C : public A {};
class D : public B, public C {};int main() {D d;d.B::data = 1; // 必须明确指定作用域,不然会产生歧义d.C::data = 2;cout << d.B::data << " " << d.C::data << endl;return 0;
}
这里类的继承关系如下:
D->B->A
->C->A
这里如果要对data赋值,必须要指明是哪个继承类的成员。如果要解决上述问题,可让类 B 和类 C 虚继承类 A,这样类 D 就只会包含一份类 A 的数据。示例如下:
#include <iostream>
using namespace std;class A {
public:int data;
};class B : virtual public A {}; // 虚继承
class C : virtual public A {}; // 虚继承
class D : public B, public C {};int main() {D d;d.data = 1; // 不会有歧义cout << d.data << endl; // 输出:1return 0;
}
起实现原理是:
- 当使用虚继承时,派生类会包含一个虚基类指针(vbptr)或者虚基类表,借助它们来定位虚基类的位置。
- 不管继承路径有多少条,虚基类的数据成员在内存中都只会存在一份。
在虚继承的情况下,虚基类的构造函数由最底层的派生类(如类 D)负责调用,中间层的派生类(如类 B 和类 C)不会调用虚基类的构造函数。例如:
#include <iostream>
using namespace std;class A {
public:A(int val) : data(val) { cout << "A constructor, val is " << val << endl; }A() { data = 6; cout << "A default constructor, data is " << data << endl;}int data;
};class B : virtual public A {
public:B() : A(1) { cout << "B constructor" << endl; } // 不会调用A的构造函数
};class C : virtual public A {
public:C() : A(2) { cout << "C constructor" << endl; } // 不会调用A的构造函数
};class D : public B, public C {
public:D() : A(3) { cout << "D constructor" << endl; } // 直接调用A的构造函数
};int main() {D d;return 0;
}
执行结果如下:
A constructor, val is 3
B constructor
C constructor
D constructor
而如果D不调用A的构造函数,则会调用A的默认构造函数,例:
#include <iostream>
using namespace std;class A {
public:A(int val) : data(val) { cout << "A constructor, val is " << val << endl; }A() { data = 6; cout << "A default constructor, data is " << data << endl;}int data;
};class B : virtual public A {
public:B() : A(1) { cout << "B constructor" << endl; } // 不会调用A的构造函数
};class C : virtual public A {
public:C() : A(2) { cout << "C constructor" << endl; } // 不会调用A的构造函数
};class D : public B, public C {
public:D() { cout << "D constructor" << endl; } // 直接调用A的构造函数
};int main() {D d;return 0;
}
结果如下:
A default constructor, data is 6
B constructor
C constructor
D constructor