C++菱形继承与虚继承的应用
菱形继承
如图所示,派生类C
继承了基类B1
和B2
,而基类B1
和B2
都是A
的派生类。
非虚继承
#include <iostream>
using std::cout;
using std::endl;
using std::string;
class A {
public:
string identity = "class A";
string baseIdentity;
A(string parent) : baseIdentity(parent) {cout << "create class A" << endl;};
};
class B1 : public A {
public:
string identity = "class B1";
B1() : A("from B1") {cout << "create class B1" << endl;};
};
class B2: public A {
public:
string identity = "class B2";
B2() : A("from B2") {cout << "create class B2" << endl;};
};
class C: public B1, public B2 {
public:
string identity = "class C";
C() : B1(), B2() {cout << "create class C" << endl;};
};
int main() {
C instacne;
cout << "!: " << instacne.identity << endl;
//cout << "!: " << instacne.A::identity << endl;
cout << "!: " << instacne.B1::identity << endl;
cout << "!: " << instacne.B2::identity << endl;
}
输出:
create class A
create class B1
create class B2
create class C
class C
class A
从中可以看出,使用非虚继承,基类A
的构造函数被构造了两次,因此,主函数int main(){...}
中被注释掉的那行代码是无法编译的,因为事实上实例instance
中B::identity
有两个独立的来源:从B1
继承来的和从B2
继承来的,所以编译器给出了如下报错:
Ambiguous conversion from derived class 'C' to base class 'A': class C -> class B1 -> class A class C -> class B2 -> class A
虚继承
#include <iostream>
using std::cout;
using std::endl;
using std::string;
class A {
public:
string identity = "class A";
string baseIdentity;
A(string parent) : baseIdentity(parent) {cout << "create class A" << endl;};
};
class B1 : virtual public A {
public:
string identity = "class B1";
B1() : A("from B1") {cout << "create class B1" << endl;};
};
class B2: virtual public A {
public:
string identity = "class B2";
B2() : A("from B2") {cout << "create class B2" << endl;};
};
class C: public B1, public B2 {
public:
string identity = "class C";
C() : B1(), B2(), A("from C") {cout << "create class C" << endl;};
};
int main() {
C instacne;
cout << "!: " << instacne.identity << endl;
cout << "!: " << instacne.A::identity << endl;
cout << "!: " << instacne.B1::identity << endl;
cout << "!: " << instacne.B2::identity << endl;
cout << "!: " << instacne.A::baseIdentity << endl;
}
输出:
create class A
create class B1
create class B2
create class C
!: class C
!: class A
!: class B1
!: class B2
!: from C
从中可以看出,使用虚继承
- 基类
A
仅被构造一次 ; - 忽略
B1
,B2
中关于A
的参数初始式; - 在派生类
C
中必须调用A
的构造函数对基类A
进行初始化 ; - 在上例中被注释掉的语句现在可以通过编译,因为建立派生类
C
时仅建立了A
的一个副本,没用歧义。