本文將通過三個經典示例程序,帶你徹底理解:

  • 多層繼承的訪問規則
  • 多源繼承的構造與析構順序
  • 菱形繼承中的二義性問題

一,多層繼承

概念

多層繼承指 一個類繼承另一個派生類,形成繼承鏈,例如:

A → B → C

C 間接繼承了 A 的成員。

示例代碼:

#include <iostream>
using namespace std;

class A
{
public:
    A() { cout << "A" << endl; }
    ~A() { cout << "~A" << endl; }
public:
    void fun_A_public() { cout << "fun_A_public" << endl; }
protected:
    void fun_A_protected() { cout << "fun_A_protected" << endl; }
private:
    void fun_A_private() { cout << "fun_A_private" << endl; }
};

class B : public A
{
public:
    B() { cout << "B" << endl; }
    ~B() { cout << "~B" << endl; }
public:
    void fun_B_public() { cout << "fun_B_public" << endl; }
protected:
    void fun_B_protected() { cout << "fun_B_protected" << endl; }
private:
    void fun_B_private() { cout << "fun_B_private" << endl; }
};

class C : public B
{
public:
    C() { cout << "C" << endl; }
    ~C() { cout << "~C" << endl; }
public:
    void fun_C_public() { cout << "fun_C_public" << endl; }
protected:
    void fun_C_protected() { cout << "fun_C_protected" << endl; }
private:
    void fun_C_private() { cout << "fun_C_private" << endl; }
};

int main()
{
    // 1. 在類外部通過類對象只能訪問public成員
    // 2. 對基類成員的訪問由繼承方式和原訪問權限共同決定
    //  (1)只有基類的public成員,以public形式繼承,那麼派生來的對象在類外部才可以訪問
	//  (2)除此之外的情況,派生來的對象在類外部都無法訪問
    C c;
    c.fun_A_public();
    c.fun_B_public();
    c.fun_C_public();

    return 0;
}
  1. 在類的內部,只能訪問基類的public、protected成員,private無法訪問
  2. 如果是多層繼承的的話,那麼子類對基類成員的訪問,只看其直接父類中成員訪問權限
  3. 父類成員在子類中存在的的訪問權限,是由繼承方式和原成員訪問權限共同決定

理解方法:

  1. 如果看子類內部是否能訪問,那麼將繼承關係的代碼改為單繼承來看。
  2. 如果看子類外部是否能訪問,那麼將繼承關係的代碼改為單個類來看
  3. 在改代碼時,將父類中的代碼複製到子類中,然後按照繼承方式修改父類成員訪問權限

運行結果:

C++中的類繼承(4)繼承種類之單繼承&多繼承&菱形繼承_#開發語言

結果分析

  1. 構造順序: A → B → C
  2. 析構順序: ~C → ~B → ~A
  3. 訪問權限:
  • 類外部只能訪問最終類對象中 public繼承下來的public成員
  • 類內部可訪問基類的 publicprotected,不能訪問 private

二,多源繼承

概念

多源繼承(或稱“多重繼承”)是指一個派生類從多個基類繼承:

class C : public A, public B

這種設計可以讓子類同時擁有多個基類的功能,但也可能帶來複雜性。

示例代碼:

#include <iostream>
using namespace std;

class A
{
public:
    A(int n) { cout << "A" << endl; }
    ~A() { cout << "~A" << endl; }
};

class B
{
public:
    B(int n) { cout << "B" << endl; }
    ~B() { cout << "~B" << endl; }
};

// 多源繼承:構造順序由繼承列表順序決定,而不是初始化列表順序
class C : public A, public B
{
public:
    C(int n) : B(n), A(n)
    {
        cout << "C" << endl;
    }
    ~C() { cout << "~C" << endl; }
};

int main()
{
    C c(5);
    return 0;
}

輸出結果:

C++中的類繼承(4)繼承種類之單繼承&多繼承&菱形繼承_#算法_02

結果分析

  1. 構造順序
    由繼承列表順序決定:A → B → C 即使初始化列表寫成 B(n), A(n),也無效。
  2. 析構順序
    與構造相反:~C → ~B → ~A
  3. 注意點
  • 構造順序只與 類定義時的繼承順序 有關;
  • 初始化列表順序不會改變這一點;
  • 若多個基類含有同名成員,會產生訪問二義性。

三,菱形繼承

概念

菱形繼承是一種特殊的多重繼承結構:

C++中的類繼承(4)繼承種類之單繼承&多繼承&菱形繼承_#算法_03

D 同時繼承 BC,而 BC 又繼承自 A
這會導致 D 中出現兩個 A 子對象,從而引發 二義性問題

示例代碼:

#include <iostream>
using namespace std;

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

    void fun_A() { cout << "fun_A" << endl; }
};

class B : public A
{
public:
    B() { cout << "B" << endl; }
    ~B() { cout << "~B" << endl; }

    void fun() { cout << "fun_B" << endl; }
};

class C : public A
{
public:
    C() { cout << "C" << endl; }
    ~C() { cout << "~C" << endl; }

    void fun() { cout << "fun_C" << endl; }
};

class D : public B, public C
{
public:
    D() { cout << "D" << endl; }
    ~D() { cout << "~D" << endl; }
};

int main()
{
    D d;
    d.B::fun_A();  // 明確指定作用域

    // d.fun_A(); // 錯誤:二義性,D 中存在兩個 A 子對象
    cout << sizeof(D) << endl;

    return 0;
}

運行結果:

C++中的類繼承(4)繼承種類之單繼承&多繼承&菱形繼承_#算法_04

結果分析

  1. D 擁有 兩個獨立的 A 子對象(來自 B 和 C)。
  2. 調用 fun_A() 時會出現二義性錯誤,需指定作用域:d.B::fun_A()
  3. 內存中 D 的大小包含了兩個 A 子對象的空間。

解決辦法:虛繼承

通過在 BC 中使用 虛繼承,可以讓 D 只保留一個共享的 A 子對象:

class B : virtual public A { ... };
class C : virtual public A { ... };

此時 D 中只有一個 A 實例,二義性問題消失,內存佔用也更小。

四、總結對比表

C++中的類繼承(4)繼承種類之單繼承&多繼承&菱形繼承_#c++_05

C++中的類繼承(4)繼承種類之單繼承&多繼承&菱形繼承_#算法_06