跳轉到

1989:Cfront 2.0——多重繼承與抽象類別#

事件#

1989 年,Cfront 2.0 發佈,C++ 語言大幅擴充。

關鍵語法/特性#

  • 多重繼承(multiple inheritance):一個類別可繼承多個基底類別。
  • 抽象類別(abstract class):含 virtual 純虛擬函式(= 0)的類別不可實例化——純介面。
  • const 成員函式:void f() const; 保證不修改物件狀態。
  • static 成員:屬於類別而非物件的成員。
  • 成員指標:int X::*p。

為何重要#

  • 多重繼承:讓「一個類別同時具備多種能力」可以靜態組合,但也帶來菱形繼承(diamond problem)的複雜性,最終在 1991 年以 虛擬繼承(Cfront 3.0)補救。
  • 抽象類別:確立「介面與實作分離」的語言層面支援——COM、CORBA 等元件模型都建立在純虛擬函式上。
  • const 成員函式:把「唯讀性」寫進型別系統,是 C++ const 正確性(const correctness) 的核心。

理論與實用原因#

  • 理論原因:多重繼承源自 Synthesizer Generator 與 Eiffel 等研究;抽象類別源自 subtype 理論。
  • 實用原因:大型專案需要「多介面實作」(如「可列印」+「可序列化」同時存在於一個類別)。

彌補了什麼缺陷#

彌補了 C++ 1.0「單一繼承無法表達多重能力」與「無法在語言層面定義純介面」的缺陷;也彌補了 C 的 const 只是「唯讀修飾詞」而非型別系統一部分的缺陷。

程式範例#

舊寫法(C++ 1.0,沒有抽象類別):只能靠慣例提供虛擬函式,但基底類別仍可被實例化,忘了改寫也不會在編譯期報錯:

class Drawable {                  // 只是「慣例上的介面」
public:
    virtual void draw();          // 有預設實作,忘了 override 不會報錯
};

Drawable d;                       // 竟然可以實例化一個「介面」!

新寫法(1989,抽象類別 + 純虛擬函式 = 0):語言層面強制介面契約:

class Drawable {                  // 抽象類別:純介面
public:
    virtual void draw() const = 0;   // 純虛擬函式,不可有實作
    virtual ~Drawable() {}
};

// Drawable d;                   // 編譯錯誤:抽象類別不可實例化

class Shape : public Drawable {   // 忘了實作 draw() 就無法實例化
    int x, y;
public:
    Shape(int x_, int y_) : x(x_), y(y_) {}
    void draw() const override {     // const 成員函式:保證不修改物件狀態
        std::cout << "Shape at (" << x << "," << y << ")\n";
    }
};

多重繼承:一個類別同時繼承兩個介面,靜態組合多種能力:

class Printable {                      // 介面一:可列印
public:
    virtual void print() const = 0;
    virtual ~Printable() {}
};

class Serializable {                   // 介面二:可序列化
public:
    virtual void save(const char* file) const = 0;
    virtual ~Serializable() {}
};

class Document : public Printable, public Serializable {  // 同時具備兩種能力
public:
    void print() const override        { /* ... */ }
    void save(const char* file) const override { /* ... */ }
};

static 成員:屬於類別而非物件,適合計數器、共用設定:

class Document {
public:
    static int count;                  // 宣告:所有物件共用
    Document()  { count++; }
    ~Document() { count--; }
};
int Document::count = 0;               // 1989 年仍需在類別外定義

相關條目#