C++菱形继承与虚继承的应用

菱形继承

如图所示,派生类C继承了基类B1B2,而基类B1B2都是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(){...}中被注释掉的那行代码是无法编译的,因为事实上实例instanceB::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仅被构造一次 ;
  • 忽略 B1B2中关于A的参数初始式;
  • 在派生类C中必须调用A的构造函数对基类A进行初始化 ;
  • 在上例中被注释掉的语句现在可以通过编译,因为建立派生类C时仅建立了A的一个副本,没用歧义。
最后修改:2020 年 07 月 06 日
如果觉得我的文章对你有用,请随意赞赏