1. 程式人生 > >C++ primer 7.5 建構函式再探

C++ primer 7.5 建構函式再探

 

 

初始值列表

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));