建構函式語義學---default constructor
C++ Annatated Reference Manual告訴我們:“default construct會在需要的時候被編譯器產生出來”。關鍵字眼在於什麼時候需要以及被誰需要。一般說來,default construct的需求方有程式設計師和編譯器。如果程式設計師需要,那麼他就有責任在程式中宣告construct function。如果是編譯器需要,那麼編譯器就會在程式中合成construct。當然,合成construct只會執行編譯器需要的行動。下面講一下編譯器合成的default construct有效的四種情況:
1)帶有“default constructor”的menber class object
如果一個類沒有任何construct,但它含有一個member object,而後者有default construct,那麼這個class的implict default construct就是“nontrival”,編譯器需要為此合成一個default constructor.不過這個合成操作只有在constructor真正呼叫時才會發生。
一個有趣的問題就發生了,編譯器如何避免在不同的編譯模組中合成出多個default constructor?就解決方法就是把合成的default construct ,destructor,assigment copy operator都已inline的方式完成。一個inline函式都有靜態連結,不會被檔案以外的部分看到。
舉個例子,在下面的程式中,編譯器為class Bar合成一個default constructor:
class Foo{
public;
Foo();
Foo(int );
}
class Bar{
public:
Foo foo;
char *str;
}
void foo_bar(){
Bar bar;//必須再次初始化
if(str){
}
}
被合成的Bar default constructor 內含有必要的程式碼,能夠呼叫Foo default constructor來處理membert object Bar::foo,但它並不產生任何程式碼來初始化Bar::str。將Bar::str初始化是程式設計師的責任,將Bar::foo初始化時編譯器的責任。被合成的default constructor看起來像這樣:
inline Bar::Bar(){
foo.Foo::Foo();
}
再一次注意,被合成的default construct是編譯器的需要,而不是程式設計師的需要。為了能讓這個程式片段真正執行起來,字串str也需要被初始化。假設程式設計師經由下面的default constructor提供str的初始化操作:
Bar::Bar(){
str=0;
}
現在程式的需求滿足了,但是編譯器還需要初始化member object foo,由於default constructor已經被明確定義出來了,編譯器沒辦法合成第二個。編譯器採取的行動是將初始化foo的程式碼插入到Bar default constructor中,使得使用者程式碼在執行之前先呼叫必要的default constructor,擴大後的程式碼可能是:
Bar::Bar(){
foo.Foo::Foo();//附加的編譯器程式碼
str=0;//explict user code
}
如果有多個class member object 都要求construct初始化操作,編譯器將如何做呢?c++語言要求以“ member object”在class中的宣告次序來呼叫各個construct,這一點由編譯器來完成。它會為每一個member object安插程式程式碼,以“member宣告次序”呼叫每一個member object的default constructor,這些程式碼將會被安插在explict user code之前。舉個例子
class Dopey{
public:
Dopey();
}
class Sneezy{
public:
Sneezy();
Sneezy(int );
}
class Snow_white{public:
Dopey dopey;
Sneezy sneezy;
private;
int number;
}
如果class Snow_white沒有定義default constructor,編譯器就會合成一個nontrival constructor被合成出來。然而,Snow_white定義了下面的default constructor。
Snow_white::Snow_white();sneezy(1023){
number=2045;
}
它會被擴張如下:
Snow_white::Snow_white();sneezy(1023){
//插入member class object
dopey.Dopey::Dopey();
sneezy.Sneezy::Sneezy(1024);
//explict user code
number=2045;
}
2)帶有default constructor的Base Class如果一個沒有任何constructor的class派生自一個帶有“default constructor”的base class,那麼derive class的default constructor會被是為nontrivial,並因此需要被合成出來。他將呼叫上一層base class的default constructor(按照他們的宣告次序),對一個後繼派生的class而言,這個合成的construtor和一個被明確提供的default constructor沒有什麼差異。
如果設計者提供多個constructor,但其中都沒有Base Class的default constructor呢?編譯器會擴張現有每一個constructor,將所有用以呼叫必要的dafault constructor的程式碼加進去,它不會合成一個新的default constructor,這是因為其它由“user 所提供的constructor存在的緣故”,如果同時存在“帶有default constructor”的membe class object,那些default constructor也會被呼叫----在所有base class constructor都被呼叫之後。