文章目錄
- 前言
- 默認建構式
- 帶有默認建構式的類物件成員
- 帶默認建構式的基類
- 帶有虛函式的類
- 總結
前言
c++ 編譯器會在人意想不到的地方做一些隱式操作,例如,只含有一個引數的建構式,會被當做型別轉換運算子,而關鍵字explict就是為了阻止這一機制,
默認建構式
c++ 編譯器會在需要的時候自動生成默認建構式,
帶有默認建構式的類物件成員
如果一個類沒有任何的建構式,但是它有一個物件成員,這個物件成員有一個默認建構式,那么編譯器將會為這個類生成一個默認建構式,但是這個生成的時機會是在這個類被呼叫的時候,
如果有兩個檔案中都呼叫了這個類,那么默認建構式將可能會被生成兩次,如何避免這種沖突呢?解決方法是把合成的建構式行內(inline, 一個行內函式有靜態鏈接 static linkage),如果建構式太復雜,不適合行內,那么就會合成一個顯式的非行內靜態物體(explicit noinline static),
例如:
class Foo {
public:
Foo()
};
class Bar {
Foo foo;
char *str;
};
// 編譯器將會為Bar合成默認建構式,初始化foo成員,但是并不會初始化str成員!!!
如果一個類Foo有自己定義的建構式(默認的,或者帶有引數的),并且這個類還有一個或者多個類成員,那么編譯器將會保證這些類成員的建構式在Foo的建構式中至少被呼叫一次,這些成員的構造順序與它們在class中的宣告順序一致,
帶默認建構式的基類
類似的,如果一個沒有建構式的派生類繼承自一個帶有默認建構式的基類,那么編譯器將會為這個派生類生成默認建構式,在這個生成的建構式中按照宣告的順序呼叫基類的建構式,
如果派生類有建構式,但是沒有默認建構式,編譯器將會為現有的建構式中安插呼叫基類建構式的代碼,但是并不會生成一個新的默認建構式,
如果這個派生類同時有類成員(這些類成員有默認建構式),那么編譯器將會在基類建構式的代碼后面安插呼叫資料成員建構式的代碼,
帶有虛函式的類
有以下兩種情況會生成默認建構式
- 類宣告或者繼承一個虛函式
- 類派生自一個繼承串鏈,其中有一個或者更多的虛基類(virtual base class)
在編譯期間,編譯器會做兩件事情:
- 生成一個虛函式表
- 為每一個物件添加一個額外的虛函式表指標,
對于類所定義的每一個建構式,編譯器將會安插一些代碼,為虛表指標賦初值,帶有一個虛基類的類編譯器會使一個虛基類在派生類中的位置 在執行期間所決定??
例如
class X{public: int i};
class A :public virtual X {public: int j;};
class B :public virtual X {public: double d;};
class C :public A, public B {public: int k;};
?
// 無法在編譯時期決定i的位置(偏移?)
void foo(const A *pa) {
pa->i = 1024;
}
?
int main ()
{
foo (new C);
foo (new A);
...
}
編譯器的一種可能的實作方式為:
在每一個派生類的虛基類中安插一個指標,經由參考或者指標來存取一個虛基類的功能都可以由這個指標來實作,
例如:
// 可能的轉換操作
void foo(const A *pa) {
pa->__vbcX->i = 1024;
}
總結
在編譯器合成的默認建構式中,會為那些有默認建構式的成員或者基類呼叫其默認建構式,而那些其他的非靜態資料成員,例如整數,指標等都不會被初始化,需要類的設計者(就是程式員了)來做這些操作,
新手一般會有兩個誤解:
- 任何類如果沒有定義默認建構式,那么就會被合成出一個來,
- 編譯器合成的默認建構式會明確設定類中的每一個資料成員初始值,
這兩個沒有一個是真的!!!
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/179209.html
標籤:其他
