1、 一個類是不是一定有構造函數?
C++ Annotated ReferenceManual(ARM):構造函數只在需要的時候纔會被編譯器產生出來。關鍵字“在需要的時候”,被誰需要?一種是程序需要的時候,一種是變編譯器需要的時候。如果程序有需要(如,完成某些非靜態數據成員的初始化),那是程序員的責任,程序員需要自己提供構造函數。如果是編譯器需要則纔會合成出來默認構造函數。編譯器不會替程序員完成任何多餘的工作。
2、 編譯器什麼情況下產生默認構造函數?產生的默認構造函數做些什麼?
1、 類A中包含類B對象的數據成員,且類B帶有默認構造函數。編譯器將會合成默認構造函數用以調用B的默認構造函數完成類B對象的初始化。
2、 類A擁有一個帶有默認構造函數的基類B,編譯器將會合成默認構造函數用以調用B的默認構造函數。(無論是明確聲明或是被合成而得)
3、 類A中包含一個虛函數時,編譯器將會合成默認構造函數用以完成對象模型中虛函數表指針的初始化。
4、 類A擁有一個虛基類時,編譯器將合成默認構造函數用以安插“允許每一個虛基類的執行期存取操作”的代碼。
備註1:如果程序員已提供默認構造函數,則編譯器將所需的操作插入該構造函數完成擴展。
備註2:C++書籍裏常見的錯誤
1> 任何class如果沒有定義默認構造函數,就會被合成出來一個
2> 編譯器合成出來的默認構造函數會明確的設定“class內每一個data member的默認值”。
如你所見,沒有一個是真的!
3、 編譯器什麼情況下產生複製構造函數?
“在必要的時候”,即class不展現出bitwise copy semantics時。
1、類A中包含類B對象的數據成員,且類B帶有複製構造函數。編譯器將會合成複製構造函數用以調用B的複製構造函數完成類B對象的初始化。
2、類A擁有一個帶有複製構造函數的基類B,編譯器將會合成複製構造函數用以調用B的複製構造函數。(無論是明確聲明或是被合成而得)
3、類A中包含一個虛函數時,編譯器將會合成複製構造函數用以完成對象模型中虛函數表指針的重新設定。
4、類A擁有一個虛基類時,編譯器將會合成複製構造函數用以完成對virtual base class的特殊處理(發生在derived class初始化base class的情況下,需要存取基類的操作)。
備註:說的更明白一些同默認構造函數產生的情況類似(一致)。
4、 複製構造函數的程序轉化。
複製構造函數的調用發生在以下三種情況下:明確的初始化操作、參數的初始化、返回值得初始化。複製構造函數的調用都伴隨着程序轉化:
1、 明確的初始化操作:
重寫每一個定義,其中的初始化操作被剝除;類的複製構造調用操作被安插進去。
X x0;
void foo_bar(){
X x1(x0);
//...
}
轉化後
void foo_bar(){
X x1; //定義重寫,初始化操作被剝除
x1.X::X(x0);
}
2、 參數的初始化
已知的函數定義:void foo(X x0);
X xx;
foo(xx);
轉化後
X __tmp0;
__tmp0.X::X(xx);
foo(__tmp0);
3、 返回值的初始化
添加額外參數,類型是class object的一個reference,用來防治被拷貝構建而得的返回值;在return之前安插一個複製構造的調用操作。
X bar(){
X xx;
//處理xx...
return xx;
}
轉化後
void bar(X& __result){
X xx; //註釋:看到沒,所謂的NRV優化就是可以將這個變量優化掉,直接處理__result;
xx.X::X();
//處理xx...
__result.X::X(xx);
return ;
}
所以 X xx=bar();將被轉化爲 X xx; bar(xx);
5、成員初始化列表(member initializationlist)
以下四種情況必須使用成員初始化列:
1、 當初始化一個引用類型的數據成員時;
2、 當初始化一個const數據成員時;
3、 當調用基類的帶參構造函數時;
4、 當調object類型的數據成員的帶參構造函數時;
前兩者因爲要求定義時初始化,所以必須明確的在初始化隊列中給它們提供初值。後兩者因爲需要顯式的調用它們的帶參構造函數而非默認構造函數在定義時初始化它們。
備註:說清楚點就是所有需要在定義時初始化的都需要使用初始化列表。