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

編譯器並不總會合成默認構造函數:揭開教科書未講的真相

     編輯:關於C語言

誤區源頭:教科書的過度簡化

大二時讀清華版C++教材,書中斬釘截鐵:「類無構造函數時編譯器自動生成默認構造函數」。直到讀到Lippman《Inside C++ Object Model》才知:這是廣為流傳的錯誤認知。Lippman直指兩大誤區:

  1. 任何無默認構造函數的類都會被合成一個;
  2. 合成的默認構造函數會顯式初始化每個數據成員。

真相:只有「必要時」才合成

編譯器僅在以下四種情況合成默認構造函數:

  1. 含有帶默認構造函數的成員類對象;
  2. 繼承自帶默認構造函數的基類;
  3. 類聲明虛函數(需初始化vfptr);
  4. 類涉及虛基類(需初始化vbptr)。

實戰驗證(VS2008)

基礎類:無任何特性

class BaseClass { public: int a, b; /*無構造函數,無虛函數*/ };
class DerivedClass : public BaseClass { public: int c; };

sizeof(BaseClass)=8, sizeof(DerivedClass)=12 → 內存正常分配。
但直接讀取 baseClass.a 觸發Runtime Error:變量未初始化。
證明:編譯器未合成默認構造函數,成員含隨機垃圾值。

加入空構造函數

class BaseClass { public: int a, b; BaseClass() {} }; // 空體

程序正常運行,輸出 -858993460 -858993460(VC++未初始化填充值 0xCCCCCCCC)。
結論:用戶聲明的空構造函數也未初始化成員,但滿足了「類有構造函數」的語義,編譯器不再報錯。

驗證條件3:虛函數觸發合成

class BaseClass { public: int a, b; virtual void show(){} }; // 無用戶構造函數

程序正常運行,sizeof(BaseClass)=12(+4字節vfptr)。
編譯器合成了默認構造函數,內部初始化vfptr指向虛函數表,但依然不初始化a、b。

驗證條件4:虛繼承觸發合成

class DerivedClass : virtual public BaseClass { public: int c; };

去掉 derivedClass.c 讀取 → Runtime Error。
改為虛繼承 → 程序正常,sizeof(DerivedClass)=16(含vbptr)。
證明:虛繼承強制編譯器合成構造函數初始化vbptr。

條件1、2驗證略(同理:成員/基類有構造函數→鏈式調用→需合成)

總結與反思

  • 合成構造函數只做編譯器必須做的事(初始化vfptr/vbptr/調用成員基類構造函數),不碰用戶數據成員。
  • 教科書錯誤誤導無數初學者以為「有默認構造函數就安全」,實則未初始化成員才是隱患源頭。
  • 最佳實踐:永遠顯式編寫構造函數並初始化所有成員,不依賴編譯器隱式行為。

本文出自「酋長」博客,轉載請保留出處。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved