1. 程式人生 > >為什麼對於類的const成員,只能使用初始化列表,而不能在建構函式內部進行賦值操作

為什麼對於類的const成員,只能使用初始化列表,而不能在建構函式內部進行賦值操作

結論:對於類的const成員,只能使用初始化列表,而不能在建構函式內部進行賦值操作。

原因如下:

1、建構函式不能被宣告為const函式,因此當我們建立一個類的const物件時,直到建構函式完成初始化的過程,物件才真正取得其“常量”的屬性,因此,建構函式在const物件的構造過程中可以向其寫值;見C++ primer P235;

2、初始化類的成員有兩種方式,一是使用初始化列表,二是在建構函式體內進行賦值操作。因此,由於常量只能初始化不能賦值,所以常量成員必須使用初始化列表;[1]

(當然你可以在類定義的時候,就對const成員變數進行賦值: class A{const int a = 1;};但是這樣操作的話,這個變數就失去了意義,即基於這個類生成的所有物件a的值都為1!

再更進一步的講,其實非const變數也可以在類定義的時候就進行賦值的操作,但是static變數不可以!)

3、主要是效能問題,對於內建型別,如int, float等,使用初始化類表和在建構函式體內初始化差別不是很大,但是對於類型別來說,最好使用初始化列表,為什麼呢?使用初始化列表少了一次呼叫預設建構函式的過程,這對於資料密集型的類來說,是非常高效的。[1]

#include <iostream>

using namespace std;

class Test1
{
public:
    Test1() {cout << "Construct Test1" << endl ;} // 無參建構函式
    Test1(const Test1& t1) // 拷貝建構函式
    {
        cout << "Copy constructor for Test1" << endl ;this->a = t1.a ;
    }
    Test1& operator = (const Test1& t1) // 過載賦值運算子(也稱為過載賦值函式)
    {
        cout << "assignment for Test1" << endl ;
        this->a = t1.a ;
        return *this;
    }
private:
    int a ;
};

class Test2
{
public:
    Test1 test1; //此處第二次呼叫Test 1的construct;
    //std::cout<<"進入Test2 的建構函式"<<std::endl; //類中只能包含成員變數和成員函式!
    Test2(Test1 &t1){test1 = t1 ;} //此處的“=”呼叫過載的“=”操作符;
};

int main(){
    Test1 t1; //此處第一次呼叫Test1的construct;
    cout<<"end of fist construct t1"<<endl;
    Test2 t2(t1);
}

輸出為:

Construct Test1
end of fist construct t1
Construct Test1
assignment for Test1

解釋一下:
第一行輸出對應呼叫程式碼中第一行,構造一個Test1物件
第三行輸出對應Test2建構函式中的程式碼,用預設的建構函式初始化物件test1 // 這就是所謂的初始化階段
第四行輸出對應Test2的賦值運算子,對test1執行賦值操作 // 這就是所謂的計算階段

如果使用初始化列表來實現Test2的建構函式;

class Test2
{
public:
    Test1 test1 ;
    Test2(Test1 &t1):test1(t1){}
};
輸出為:
Construct Test1
end of fist construct t1
Copy constructor for Test1

第一行輸出對應 呼叫程式碼的第一行
第三行輸出對應Test2的初始化列表,直接呼叫拷貝建構函式初始化test1,省去了呼叫預設建構函式的過程。
所以一個好的原則是,能使用初始化列表的時候儘量使用初始化列表

參考:

[1] 初始化列表