為什麼對於類的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] 初始化列表