天天看點

C++ 構造函數初始化清單

構造函數初始化清單以一個冒号開始,接着是以逗号分隔的資料成員清單,每個資料成員後面跟一個放在括号中的初始化式。例如:

class A
{
public:
    int a;
    float b;
    A(): a(0),b(9.9) {} //構造函數初始化清單
};

class A
{
public:
    int a;
    float b;
    A()   //構造函數内部指派
    {
        a = 0;
        b = 9.9;
    }
};      

上面的例子中兩個構造函數的效果是一樣的。使用初始化清單的構造函數是顯示地初始化類的成員;而沒有使用初始化清單的構造函數是對類的成員指派,并沒有顯示地初始化。

初始化清單的構造函數和内部指派的構造函數對内置類型的成員沒有什麼大的差別,像上面的任一個構造函數都可以。

用構造函數的初始化清單來進行初始化,寫法友善、簡練,尤其當需要初始化的資料成員較多時更顯其優越性。對非内置類型成員變量,推薦使用類構造函數初始化清單。

但有的時候必須用帶有初始化清單的構造函數:(1)沒有預設構造函數的成員類對象;(2)const成員或引用類型的成員。

       構造函數中有着比我們所看見的還要多的細節,構造函數可以調用其它的構造函數來初始化對象中的基類對象和成員對象的構造函數。

類的資料成員中的其它類對象,若該成員類型是沒有預設構造函數,則必須進行顯示初始化,因為編譯器會隐式調用成員類型的預設構造函數,而它又沒有預設構造函數,則編譯器嘗試使用預設構造函數将會失敗。

例:

class A
{
public:
    A (int x)
    {
        i = x;    // 無預設構造函數
    }
private:
    int i;
};
class B
{
public:
    B(int y)
    {
        j = y;    // error C2512: “A”: 沒有合适的預設構造函數可用
    }
private:
    A a;
    int j;
};
int main( )
{
    B b(5);
    return 0;
}      

B類資料成員中有一個A類對象a,建立B類對象時,要先建立其成員對象a;A類有一個參數化大的構造函數,則編譯器不會提供預設無參數的構造函數,是以a無法建立。

對成員對象正确的初始化方法是通過顯示方式進行,B的構造函數應該寫成:

B(int y, int z) : a(z)
{
    j = y;
}
B b(5,10);      

構造函數初始化清單是初始化常資料成員和引用成員的唯一方式。因為const對象或引用類型隻能初始化,不能對他們指派。

class A
{
public:
    A (int x,int y) : c(x),j(y)     // 構造函數初始化清單
    {
        i = -1;
    }
private:
    int i;
    const int c;
    int& j;
};
int main( )
{
    int m;
    A a(5,m);
    return 0;
}      

若不通過初始化清單來對常資料成員和引用成員進行初始化:

class A
{
public:
    A (int x)      // 構造函數初始化清單
    {
        i = -1;
        c = 5;
        j = x;
    }
private:
    int i;
    const int c;  // error C2758: “A::c”: 必須在構造函數基/成員初始值設定項清單中初始化
    int& j;     // error C2758: “A::j”: 必須在構造函數基/成員初始值設定項清單中初始化
};      

預設情況下,在構造函數的被執行前,對象中的所有成員都已經被它們的預設構造函數初始化了。當類中某個資料成員本身也是一個類對象時,我們應該避免使用指派操作來對該成員進行初始化:

class Person
{
private:
    string name;
public:
    Person(string& n)
    {
        name = n;
    }
}      

雖然這樣的構造函數也能達到正确的結果,但這樣寫效率并不高。當一個Person對象建立時,string類成員對象name先會被預設構造函數進行初始化,然後在Person類構造函數中,它的值又會因指派操作而在改變一次。我們可以通過初始化清單來顯示地對name進行初始化,這樣便将上邊的兩步(初始化和指派)合并到一個步驟中了。

class Person
{
private:
    string name;
public:
    Person(string& n): name(n){}
}      

作者:王陸