C++中類的建構函式與複製建構函式
1 相關定義
1.1 建構函式
建構函式是類的特殊的成員函式,只要建立類型別的新物件,都要執行建構函式。建構函式的工作是保證每個物件的資料成員具有合適的初始值。建構函式的名字與類的名字相同,並且不能指定返回型別。像其他任何函式一樣,它們可以沒有形參,也可以定義多個形參。
1.2 複製建構函式
複製建構函式是一種特殊建構函式,具有單個形參,該形參(常用const修飾)是對該類型別的引用。當定義一個新物件並用一個同類型的物件對它進行初始化時,將顯式使用複製建構函式。
2 兩種初始化方式
C++支援兩種初始化方式:直接初始化和複製初始化。
2.1 兩種初始化方式的實現
複製初始化使用=符號,而直接初始化將初始化式放在圓括號中。
自定義一個名為MyClass的類。並且定義MyClass類的預設建構函式、自定義建構函式和複製建構函式。
class MyClass { public: MyClass() { cout << "進入MyClass的預設建構函式" << endl; } MyClass(const char* pc) { cout << "進入MyClass自定義建構函式" << endl; } MyClass(const ClassTest& ct) { cout << "進入MyClass複製建構函式" << endl; } };
接下來使用直接初始化和複製初始化定義MyClass類的物件:
MyClass mc1(“myclass”);
MyClass mc2 = mc1;
執行後的效果如圖所示
從上圖可以看出,第1行程式碼呼叫了MyClass類的自定義建構函式,第2行程式碼呼叫了MyClass類的複製建構函式。
2.2 兩種初始化方式的關係
對於MyClass類物件的定義,還有一種方式,如下所示
MyClass mc3 = “myclass”;
這種定義方式是採用了哪種初始化呢?《C++ Primer中文版第4版》P407頁提到,以上這種定義方式,“編譯器首先呼叫接受一個C風格字串形參的建構函式,建立一個臨時物件,然後,編譯器使用複製建構函式將類的物件初始化為那個臨時物件的副本”。也就是說,以上程式碼應該呼叫了
此時,只調用了MyClass類的自定義函式,而沒有呼叫複製建構函式。產生這個現象的原因,在網路上有朋友提到“主要原因在於編譯器的優化,當複製建構函式是public時,編譯器就會根據這個特性來對程式碼進行優化。當程式執行時,編譯器發現複製建構函式是public,則說明程式允許物件之間的複製,此時就會通過直接呼叫自定義建構函式來初始化物件,而不再呼叫複製建構函式,完成優化”。這位朋友還提到,如果將複製建構函式改為private,此時如下程式碼
MyClass mc3 = “myclass”;
編譯時就會報錯。
但是,至少在VC++6.0及以上版本的編譯器中,並不是這樣的。在VC++6.0/VS2005/VS2008/VS2010/VS2015中進行測試,當將MyClass類的複製建構函式改為private後,上述程式碼編譯時並不報錯,而且都僅顯示“呼叫了自定義建構函式”。這就說明,至少在VC++6.0/VS2005/VS2008/VS2010/VS2015的編譯器當中,如下程式碼
MyClass mc3 = “myclass”;
並沒有像《C++ Primer中文版第4版》中提到的那樣,呼叫了兩次建構函式,而是隻呼叫了自定義的建構函式。可以說,在VC++6.0/VS2005/VS2008/VS2010/VS2015中
MyClass mc1(“myclass”);
與
MyClass mc3 = “myclass”;
是等價的。
3 編譯器優化
在VC++6.0/VS2005/VS2008/VS2010/VS2015中進行測試時,確實發現了在“2.2”中提到的編譯器優化問題。對於如下程式碼
MyClass mc4 = MyClass();
在VC++6.0中進行測試時,執行結果顯示編譯器先呼叫MyClass類預設建構函式,之後呼叫複製建構函式;而在VS2005/VS2008/VS2010/VS2015中則只顯示編譯器呼叫了預設建構函式。並且如果將複製建構函式設定為private,編譯器會報錯,如下所示:
error C2248: “ClassTest::ClassTest”: 無法訪問 private 成員(在“ClassTest”類中宣告)
這就說明,在VS2005/VS2008/VS2010/VS2015中,編譯器進行了優化,當發現複製建構函式是public時,直接呼叫預設建構函式。而當複製建構函式是private時,就會報錯。