首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

C++ 避免重新定義派生類別的非虛擬函式

     編輯:關於C語言

今天無意中發現一個關於 C++ 基礎的問題,當時愣是沒理解原因,現在搞明白了就記錄下來。先看小程式,先實踐再理論。

  1. #include
  2. using namespace std;
  3. class Base
  4. {
  5. public:
  6. virtual void funtion(int arg = 1){ cout << arg << endl; }
  7. };
  8. class Derive : public Base
  9. {
  10. public:
  11. virtual void funtion(){ cout << "Derive" << endl; }
  12. virtual void funtion(int arg){ cout << "Derive" << arg << endl; }
  13. };
  14. int main(int argc, char *argv[])
  15. {
  16. Base* obj = new Derive();
  17. obj->funtion();
  18. system("pause");
  19. return 0;
  20. }

執行結果會是什麼?很多人會懷疑程式錯誤,但實際執行結果是呼叫了子類別的函式,且子類別中 arg 參數的值是父類別中的預設值 1,輸出「Derive 1」。

原因分析

首先要理解物件的兩種類型:動態類型和靜態類型。

  • 靜態類型:指標或引用宣告時的類型
  • 動態類型:由實際指向的類型決定
  1. Base *pgo = ; // pgo 靜態類型是 Base*
  2. new Derive; // 動態類型是 Derive*
  3. Asterioid *pa = new Asterioid; // pa 靜態類型是 Asterioid*
  4. // 動態類型也是 Asterioid*
  5. pgo = pa; // pgo 靜態類型仍指向 Base*
  6. // 動態類型指向了 Asterioid*
  7. Base &rgo = *pa; // rgo 靜態類型是 Base
  8. // 動態類型是 Asterioid

虛擬函式是動態綁定的,而預設參數值是靜態綁定的。為了執行效率,如果預設參數值是動態綁定的,編譯器必須提供一整套方案為執行時的虛擬函式參數確定恰當的預設值,這會比 C++ 當前的編譯時決定機制更複雜、更慢。C++ 選擇了速度和簡潔,若忽略此建議就會陷入困惑。

《Effective C++ 第3版》第 36 條明確指出:避免對派生的非虛擬函數進行重定義。書中詳細推導了這一命題的理論基礎:公有繼承意味著「is-a」關係,非虛擬函數代表類別的「個性化壁壘」。若在派生類別中重新定義非虛擬函數,會導致設計矛盾——要麼不是真正的公有繼承,要麼該函數應該是虛擬的。

銘記在心

  • 避免在派生類別中重新定義非虛擬函數
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved