Java 提供 final 關鍵字禁止類被繼承,C++ 雖無直接對應語法(C++11 前),卻可透過建構子存取控制、CRTP 機制或現代 final 關鍵字達成同樣目標。本文由淺入深解析三種主流實作,並對比其優缺點與適用場景。
核心思想:將建構子與解構子宣告為 private,外部無法直接建構或銷毀物件,衍生類因無法呼叫基底類建構子而編譯失敗。提供 static 工廠方法控制物件生命週期:
class FinalClass1 {
public:
static FinalClass1* GetInstance() { return new FinalClass1; }
static void DeleteInstance(FinalClass1* p) { delete p; p = 0; }
private:
FinalClass1() {}
~FinalClass1() {}
};
缺點:僅能動態配置於堆疊,無法建立堆疊物件;使用介面與一般類別差異大,需手動管理記憶體。
利用模板基底類別 MakeFinal<T> 將建構子/解構子設為 private,並宣告目標類別 T 為 friend。目標類別以 virtual public 繼承模板基底:
template <typename T>
class MakeFinal {
friend T;
private:
MakeFinal() {}
~MakeFinal() {}
};
class FinalClass2 : virtual public MakeFinal<FinalClass2> {
public:
FinalClass2() {}
~FinalClass2() {}
};
原理:虛擬繼承導致最衍生類直接呼叫虛擬基底建構子。當嘗試繼承 FinalClass2 並實例化時,編譯器嘗試呼叫 MakeFinal<FinalClass2> 的私有建構子,因非友元而失敗:
class Try : public FinalClass2 { // 編譯錯誤:無法存取 MakeFinal 私有建構子
public:
Try() {}
~Try() {}
};
Try temp; // 錯誤發生於此
優點:使用語法與一般類別完全一致,支援堆疊/堆疊配置,零額外執行期開銷。
final 關鍵字(現代標準做法)C++11 引入 final 指定符,可修飾類別或虛擬函數:
struct A {
virtual void foo() final; // 禁止進一步覆寫
};
struct B final : A { // 禁止被繼承
void foo(); // Error: foo 在 A 中已標記 final
};
struct C : B { } // Error: B 是 final 類別
優勢:語意明確、編譯器強制執行、無需樣板程式碼、與標準庫/第三方庫完美互操作。
| 場景 | 推薦方案 | 理由 |
|---|---|---|
| C++11 或更新編譯器 | final 關鍵字 | 標準、簡潔、零成本 |
| 舊標準 (C++98/03) 需支援堆疊物件 | CRTP + 虛擬繼承 | 介面自然、效能最佳 |
| 舊標準且接受僅限堆疊配置 | 私有建構子 + 工廠 | 實作最簡單 |
掌握這三種技術,可根據專案編譯器限制與設計需求,靈活實現「不可繼承」語意。