1、關(guān)于構(gòu)造函數(shù) 1)用構(gòu)造函數(shù)確保初始化 對于一個空類 class Empty { };
編譯器會自動聲明4個默認(rèn)函數(shù):構(gòu)造函數(shù),,拷貝構(gòu)造函數(shù),,賦值函數(shù),析構(gòu)函數(shù)(當(dāng)然,,如果不想使用自動生成的函數(shù),,就應(yīng)該明確拒絕),這些生成的函數(shù)都是public且inline,。構(gòu)造函數(shù)對數(shù)據(jù)成員進(jìn)行初始化,,使用未初始化值可能導(dǎo)致無法預(yù)知的錯誤,所以,,確保每一個構(gòu)造函數(shù)都將每一個成員初始化,。 2)為什么構(gòu)造函數(shù)不能有返回值 如果有返回值,要么編譯器必須知道怎么處理返回值,,要么就客戶程序員顯式調(diào)用構(gòu)造函數(shù)和析構(gòu)函數(shù),,這樣,還有安全性么,? 3)為什么構(gòu)造函數(shù)不能為虛函數(shù) 簡單來說,,虛函數(shù)調(diào)用的機(jī)制,是知道接口而不知道其準(zhǔn)確對象類型的函數(shù),,但是創(chuàng)建一個對象,,必須知道對象的準(zhǔn)確類型;當(dāng)一個構(gòu)造函數(shù)被調(diào)用時,,它做的首要事情之一就是初始化它的VPTR來指向VTABLE,。 4)構(gòu)造函數(shù)的一個面試題:
#include <iostream> using namespace std; class Base
{ private: int i; public: Base(int x) { i = x; } }; class Derived : public Base
{ private: int i; public: Derived(int x, int y) { i = x; } void print() { cout << i + Base::i << endl; } }; int main()
{ Derived A(2,3); A.print(); return 0; } 首先,是訪問權(quán)限問題,,子類中直接訪問Base::i是不允許的,,應(yīng)該將父類的改為protected或者public(最好用protected)
其次,,統(tǒng)計父類和子類i的和,,但是通過子類構(gòu)造函數(shù)沒有對父類變量進(jìn)行初始化;此處編譯會找不到構(gòu)造函數(shù),,因為子類調(diào)用構(gòu)造函數(shù)會先找父類構(gòu)造函數(shù),,但是沒有2個參數(shù)的,所以可以在初始化列表中調(diào)用父類構(gòu)造函數(shù) 最后個問題,,是單參數(shù)的構(gòu)造函數(shù),,可能存在隱式轉(zhuǎn)換的問題,,因為單參數(shù)構(gòu)造函數(shù),和拷貝構(gòu)造函數(shù)形式類似,,調(diào)用時很可能會發(fā)生隱式轉(zhuǎn)換,,應(yīng)加上explicit關(guān)鍵字,修改后如下
#include <iostream> using namespace std; class Base
{ protected: int i; public: explicit Base(int x) { i = x; } }; class Derived : public Base
{ private: int i; public: Derived(int x, int y):Base(x) { i = y; } void print() { cout << i + Base::i << endl; } }; int main()
{ Derived A(2,3); A.print(); return 0; } 2,、初始化列表 1)使用初始化列表提高效率 常用的初始化可能如下:
class Student { public: Student(string in_name, int in_age) { name = in_name; age = in_age; } private : string name; int age; }; 這么寫,,可以達(dá)到預(yù)期效果,不過不是最佳做法,,因為在構(gòu)造函數(shù)中,,是對name進(jìn)行賦值,不是初始化,,而string對象會先調(diào)用它的默認(rèn)構(gòu)造函數(shù),,再調(diào)用string類(貌似是basic_string類)的賦值構(gòu)造函數(shù);對于上例的age,,因為int是內(nèi)置類型,,應(yīng)該是賦值的時候獲得了初值。
要對成員進(jìn)行初始化,,而不是賦值,,可以采用初始化列表(member initialization list)改寫為如下:
class Student { public: Student(string in_name, int in_age):name(in_name),age(in_age) {} private : string name; int age; }; 結(jié)果與上例相同,不過在初始化的時候調(diào)用的是string的拷貝構(gòu)造函數(shù),,而上例會調(diào)用兩次構(gòu)造函數(shù),,從性能上會有不小提升 有的情況下,是必須使用初始化列表進(jìn)行初始化的:const對象,、引用對象 2)初始化列表初始順序 考慮以下代碼:
#include <iostream> using namespace std; class Base
{ public: Base(int i) : m_j(i), m_i(m_j) {} Base() : m_j(0), m_i(m_j) {} int get_i() const { return m_i; } int get_j() const { return m_j; } private:
int m_i; int m_j; };
int main()
{ Base obj(98); cout << obj.get_i() << endl << obj.get_j() << endl; return 0; } 輸出為一個隨機(jī)數(shù)和98,,為什么呢?因為對于初始化列表而言,,對成員變量的初始化,,是嚴(yán)格按照聲明次序,而不是在初始化列表中的順序進(jìn)行初始化,,如果改為賦值初始化則不會出現(xiàn)這個問題,,當(dāng)然,為了使用初始化列表,,還是嚴(yán)格注意聲明順序吧,,比如先聲明數(shù)組大小,再聲明數(shù)組這樣,。 |
|