看這樣一個模板,它生成的類使得一個名字和一個 T 類型的對象的指針關(guān)聯(lián)起來。 template<class T> class NamedPtr { public: NamedPtr(const string& initName, T *initPtr); ... private: string name; T *ptr; }; 因?yàn)橛兄羔槼蓡T的對象在進(jìn)行拷貝和賦值操作時可能會引起指針混亂,NamedPtr 也必須實(shí)現(xiàn)這些函數(shù)在寫 NamedPtr 構(gòu)造函數(shù)時,必須將參數(shù)值傳給相應(yīng)的數(shù)據(jù)成員。有兩種方法來實(shí)現(xiàn)。 第一種方法是使用成員初始化列表: template<class T> NamedPtr<T>::NamedPtr(const string& initName, T *initPtr ) : name(initName), ptr(initPtr) {} 第二種方法是在構(gòu)造函數(shù)體內(nèi)賦值: template<class T> NamedPtr<T>::NamedPtr(const string& initName, T *initPtr) { name = initName; ptr = initPtr; } 兩種方法有重大的不同。 從純實(shí)際應(yīng)用的角度來看,有些情況下必須用初始化。特別是 const 和引用數(shù)據(jù)成員只能用初始化,不能被賦值。所以,如果想讓NamedPtr<T>對象不能改變它的名字或指針成員,就必須遵循條款21 的建議聲明成員為const: template<class T> class NamedPtr { public: NamedPtr(const string& initName, T *initPtr); ... private: const string name; T * const ptr; }; 這個類的定義要求使用一個成員初始化列表,因?yàn)?const 成員只能被初始化,不能被賦值。如果 NamedPtr<T>對象包含一個現(xiàn)有名字的引用,情況會非常不同。但還是要在構(gòu)造函數(shù)的初始化列表里對引用進(jìn)行初始化。還可以對名字同時聲明const 和引用,這樣就生成了一個其名字成員在類外可以被修改而在內(nèi)部是只讀的對象。 template<class T> class NamedPtr { public: NamedPtr(const string& initName, T *initPtr); ... private: const string& name; // 必須通過成員初始化列表 // 進(jìn)行初始化 T * const ptr; // 必須通過成員初始化列表 // 進(jìn)行初始化 }; 然而前面最初的類模板不包含const 和引用成員。即使這樣,用成員初始化列表還是比在構(gòu)造函數(shù)里賦值要好。這次的原因在于效率。當(dāng)使用成員初始化列表時,只有一個string 成員函數(shù)被調(diào)用。而在構(gòu)造函數(shù)里賦值時,將有兩個被調(diào)用。為了理解為什么,請看在聲明NamedPtr<T>對象時都發(fā)生了些什么。對象的創(chuàng)建分兩步: 1. 數(shù)據(jù)成員初始化。 2. 執(zhí)行被調(diào)用構(gòu)造函數(shù)體內(nèi)的動作。 對有基類的對象來說,基類的成員初始化和構(gòu)造函數(shù)體的執(zhí)行發(fā)生在派生類的成員初始化和構(gòu)造函數(shù)體的執(zhí)行之前 對 NamedPtr 類來說,這意味著string 對象name 的構(gòu)造函數(shù)總是在程序執(zhí)行到NamedPtr 的構(gòu)造函數(shù)體之前就已經(jīng)被調(diào)用了。問題只在于:string 的哪個構(gòu)造函數(shù)會被調(diào)用?這取決于 NamedPtr 類的成員初始化列表。如果沒有為name 指定初始化 參數(shù),string 的缺省構(gòu)造函數(shù)會被調(diào)用。當(dāng)在NamedPtr 的構(gòu)造函數(shù)里對name執(zhí)行賦值時,會對name 調(diào)用operator=函數(shù)。這樣總共有兩次對string 的成員函數(shù)的調(diào)用:一次是缺省構(gòu)造函數(shù),另一次是賦值。相反,如果用一個成員初始化列表來指定name 必須用initName 來初始化,name 就會通過拷貝構(gòu)造函數(shù)以僅一個函數(shù)調(diào)用的代價被初始化。 即使是一個很簡單的 string 類型,不必要的函數(shù)調(diào)用也會造成很高的代價。隨著類越來越大,越來越復(fù)雜,它們的構(gòu)造函數(shù)也越來越大而復(fù)雜,那么對象創(chuàng)建的代價也越來越高。養(yǎng)成盡可能使用成員初始化列表的習(xí)慣,不但可以滿足const 和引用成員初始化的要求,還可以大大減少低效地初始化數(shù)據(jù)成員的機(jī)會。
|