C++ primer 7.5 建構函式再探
阿新 • • 發佈:2018-12-08
初始值列表
Sales_data::Sales_data(const string &s) : bookNo(s)
{
}
Sales_data::Sales_data(const string &s)
{
bookNo = s;
}
這兩個函式從執行的結果上看沒有區別,但是
第一個函式執行的過程相當於
string foo = "abc";
定義並初始化
第二個函式執行的過程相當於
string foo;
foo = "abc";
先定義、再賦值,且在定義時會執行預設初始化
第一個過程明顯優於第二個
如果沒有在建構函式的初始值列表中顯式地初始化成員,則該成員將在建構函式體之前執行預設初始化
因此推薦使用初始值列表
初始值有時必不可少
在宣告變數明:
- 對於const或者引用型別的變數,必須初始化。
- 對於沒有定義預設建構函式的類,其物件必須初始化
類似的,當這些型別的變數是類的成員時,必須在初始值列表中初始化
初始化的順序
並不是寫在初始值列表前面的成員先被初始化
成員的初始化順序與它們在類定義中出現的順序一致
為了減少不必要的麻煩,儘量避免依賴於順序的初始值列表
class X { private: int i; int j; public: X(int val) : j(val), i(j){} };
儘量避免依賴於順序,修改為
X(int val) : j(val), i(val){}
預設實參與建構函式
class Sales_data
{
public:
Sales_data(std::string s = "") : bookNo(s){}
};
如果一個建構函式為所有引數都提供了預設實參,則它實際上也定義了預設建構函式
委託建構函式
c++11提供
一個委託構造使用它所屬類的其他建構函式執行自己的初始化過程
class Sales_data { public: //這是函式一,是一個普通的建構函式 Sales_data(string s, unsigned cnt, double price) : bookNo(s), units_sold(cnt), revenue(cnt*price){} //接下來就是各種偷懶方法了,注意看 Sales_data(): Sales_data("", 0, 0){} //函式二是預設建構函式,委託函式一幫忙初始化,也可以認為是呼叫了函式一 Sales_data(string s): Sales_data(s, 0, 0){} //函式三接受一個string引數,委託函式一幫忙初始化 Sales_data(istream &is): Sales_data() { read(is, *this); } //函式四複雜些,它先委託函式二,就是預設建構函式,函式二去委託函式一,這些函式執行完成後,再執行函式四的函式體 //呼叫read函式讀取給定的istream };
後面的幾個建構函式都委託第一個建構函式執行自己的初始化過程
委託建構函式的執行過程:
- 被委託者的初始化列表
- 被委託者的函式體
- 委託者的函式體
預設建構函式的作用
當物件被預設初始化或值初始化時自動執行預設建構函式。
在實際中,如果定義了其他建構函式,最好也提供一個預設建構函式。
Sales_data obj;
執行預設建構函式的邏輯
隱式的類型別轉換
如果建構函式只接受一個引數,則它實際上定義了轉換為此類型別的隱式轉換機制。我們把這種建構函式稱作轉換建構函式。
string null_book = "999";
item.combine(null_book);
上面的程式碼是合法的,但是我們知道combine的引數實際應當是Sales_data型別
Sales_data &combine(const Sales_data&);
所以這裡存在一個隱式的類型別轉換過程:
- 編譯器用給定的string自動建立了一個Sales_data物件
- 新生成的這個臨時物件被傳遞給combine
只允許一步類型別轉換
編譯器只會自動地執行一步型別轉換
下面的程式碼隱式地使用了兩種轉換規則,所以它是錯誤的
item.combine("999");
正確的寫法是
item.combine(string("999");
item.combine(Sales_data("999"));
抑制建構函式定義的隱式轉換
通過將建構函式宣告為explicit來阻止
- 關鍵字explicit只對一個實參的建構函式有效
- 在類外部定義成員函式時不需要使用explicit關鍵字
class Sales_data
{
public:
Sales_data() = default;
explicit Sales_data(const string &s): bookNo(s){}
explicit Sales_data(istream&);
};
Sales_data item;
string b = "1"
item.combine(b); //這樣就不行了
explicit Sales_data::Sales_data(istream& is){} //這樣也不行,在外面了
Sales_data item1(b); //這樣可以
Sales_data item2 = item1; //不行,explicit宣告的不能拷貝初始化
顯式轉換
item.combine(Sales_data(null_book));
item.combine(static_cast<Sales_data>(cin));