class Word { private: String _name; int _cnt; public: // 沒有錯誤,但是太naive Word() { _name = 0; _cnt = 0; } };在這裡,Word構造函數會先產生一個臨時性的String對象,然後將它初始化,再以一個賦值運算符將臨時對象指定給_name,然後再銷毀臨時對象。以下是構造函數可能的內部擴張結果:
// C++偽代碼 Word::Word( /* This pointer goes here */) { // 調用String的default constructor _name.String::String(); // 產生臨時對象 String temp = String(0); // memberwise地拷貝_name _name.String::operator=(temp); // 銷毀臨時對象 temp.String::~String(); _cnt = 0; }對程序代碼反復審查並修正,得到一個明顯更有效率的實現方法:
// 較佳的方式 Word::Word : _name(0) { _cnt = 0; } 它會被擴張成這樣子: // C++偽代碼 Word::Word( /* This pointer goes here */ ) { // 調用String(int) constructor _name.String::String(0); _cnt = 0; }順便一提,陷阱最可能發生在這種形式的template code中:
template這會引導某些程序員十分積極進取地堅持所有的成員初始化操作必須在成員初始化列表中完成,甚至即使是一個行為良好的成員如_cnt:foo ::foo(type t) { // 可能是也可能不是個好主意 // 視type的真正類型而定 _t = 0; }
// 堅持此種代碼風格 Word::Word() : _cnt(0), _name(0) {}成員初始化列表中到底發生了什麼事情?許多人對list的語法感到迷惑,誤以為它是一組函數調用,當然不是!
// C++偽代碼 Word::Word( /* this pointer goes here */ ) { _name.String::String(0); _cnt = 0; }它看起來很像是在構造函數中指定_cnt的值,事實上,有一些微妙的地方要注意:列表中的項目次序是由類中成員的聲明次序決定的,而不是初始化列表中的排列順序決定的。在本例的Word類中,_name被聲明在_cnt之前,所以它的初始化比_cnt早。
class X { private: int i; int j; public: X (int val) : j(val), i(j) {} };上述程序代碼看起來好像要把j設初值為val,然後把i設初值為j。但是由於聲明順序的原因,初始化列表中的i(j)比j(val)更早執行。而j開始並沒有初始值,所以i(j)的執行結果導致i會被初始化為一個無法預測的值。
// 一個有趣的問題 X::x(int val) : j(val) { i = j; }j的初始化順序會插入在顯式用戶賦值操作(i=j)之前還是之後呢?
// X::xfoo()被調用 X::X(int val) : i(xfoo(val)), j(val) {}其中xfoo()是X的一個成員函數,答案是yes.但是最好使用存在於構造函數提內的一個成員,而不要使用存在於成員初始化列表中的成員,來為另一個成員設定初始值.並不確定xfoo()對X object的依賴性有多高,如果把xfoo()放在構造函數體內,那麼對於到底哪一個member在xfoo()執行時被設置初始值這件事,就可以給出確定的答案.
// constructor擴張後的結果 X::X( /* this pointer, */ int val) { i = this->xfoo(val); j = val; }如果一個派生類成員函數被調用,其返回值被當作基類構造函數的一個參數,將會如何?
// 調用FooBar::fval()可以嗎? class FooBar : public X { private: int _fval; public: int fval() { return _fval; } // derived class member function FooBar(int val) : _fval(val), X(fval()) // fval()作為base class constructor的參數 {} };下面是它可能的擴張結果:
// C++偽代碼 FooBar::FooBar( /* this pointer goes here */ ) { X::X(this, this->fval()); _fval = val; }它的確不是一個好主意.