先從我最近遇到的一個詭異的現象說起,來看一段代碼:
#include <iostream>using namespace std;class test{public: test(){} test(test &t) { data = t.data; } test func(test t) { test m; return m; }public: double data;};int main(){ test t1,t2; test t3 = t1.func(t2); return 0;}gcc 編譯結果如下:
是不是有點不懂是啥意思,其實大體意思是拷貝建構函式提供的函數參數與所需要的不符合,但是拷貝建構函式的形式參數只能為自身類對象的引用而不能為對象(原因待會後面會闡述),而這裡的“.....for call to "test::test(test)"”就不太能理解了,,那好吧,,,我們在做一個實驗,,將拷貝建構函式改成 test(test t) { data = t.data; }重新編譯,結果如下:
哈,意料之中,還是編譯通不過,因為拷貝建構函式的函數形式參數不可以為自身對象,最多為引用(原因後述),不過現在也不是一無所獲,這次編譯直接提示你可能需要的類型為"test (const test&)",那就再改吧,按照提示,如下改成const test& t test(const test &t) { data = t.data; }這次編譯成功通過。
為了準確的定位問題原因,我們再做一個實驗,將整體代碼改成如下形式#include <iostream>using namespace std;class test{public: test(){} test(test &t) { data = t.data; } test func(test t) { test m; return m; }public: double data;};int main(){ test t1,t2;///////////////////////// test t3; t1.func(t2);//////////////////////// return 0;}編譯通過,這次代碼,拷貝建構函式依舊是test &形參,只不過,main函數裡調用func的時候我沒有用一個對象去接受傳回值,這樣子就通過了,而之前用一個物件變數去接受函數返回就失敗。
好了,現在能夠定位錯誤了,最關鍵的問題就在於 func函數傳回值上,根據上述的實驗,函數將對象作為傳回值並且賦予外部的另一個變數的時候,是調用了類的拷貝建構函式,而且此時的拷貝建構函式的參數是“const myclass &"形式的"myclass &"都不行,為啥呢? 當時到這一步的時候本人就很困惑,為何此時一定要用const呢???
------------------------------------------華麗的分割----------------------------------------
先回味一下c++拷貝建構函式的一些特性:
調用拷貝建構函式的情形
在C++中,下面三種對象需要調用拷貝建構函式(有時也稱“複製建構函式”):
1)
一個對象作為函數參數,以值傳遞的方式傳入函數體;
2) 一個對象作為函數傳回值,以值傳遞的方式從函數返回;
3)
一個對象用於給另外一個對象進行初始化(常稱為複製初始化);
如果在前兩種情況不使用拷貝建構函式的時候,就會導致一個指標指向已經被刪除的記憶體空間。對於第三種情況來說,初始化和賦值的不同含義是拷貝建構函式調用的原因。事實上,拷貝建構函式是由普通建構函式和賦值操作符共同實現的。描述拷貝建構函式和賦值運算子的異同的參考資料有很多。
通常的原則是:①對於凡是包含動態分配成員或包含指標成員的類都應該提供拷貝建構函式;②在提供拷貝建構函式的同時,還應該考慮重載"="賦值操作符號。原因詳見後文。
拷貝建構函式必須以引用的形式傳遞(參數為引用值)