拷貝構造函數應用的場合由以下幾個方面:
1 函數的參數是一個對象,並且是值傳遞方式
2 函數的返回值是一個對象,並且是值傳遞方式
3 用一個對象初始化另外一個對象
由此,當函數的參數或者返回值為一個對象時,使用的時候要小心,因為值傳遞的時候執行的是位拷貝,並不會調用對象的構造函數,也就是說生成的臨時對象可能不是正確初始化的,這樣就可能會出現一些意向不到的問題。當返回值是個對象和用一個對象初始化另外一個對象時的情況是相同的。
比如如下代碼:
#include <iostream>
using namespace std;
class CTest
{
public:
int i;
CTest(){cout << "construct" << endl;}
~CTest(){cout << "discontruct" << endl;}
};
void test(CTest obj)
{
}
int main()
{
CTest testObj;
test(testObj);
return 0;
}
這個程序運行的結果為:
construct
discontruct
discontruct
調用了一次構造函數,調用了兩次析構函數。聲明testObj對象時,調用了一次構造函數。當testObj以值傳遞的方式傳入test函數時,此時會生成一個CTest類型的臨時變量,但是此時編譯器采用的是位拷貝的方式,並不調用CTest類的構造函數。當test函數退出時,生成的臨時變量生命周期結束,調用一次析構函數,當main函數退出時,testObj變量生命周期結束,調用一次析構函數。所以出現上面的輸出。
正確的解決方法是定義一個拷貝構造函數。
拷貝構造函數的類型為:YourClass&(YourClass& object);
修改後的代碼為:
#include <iostream>
using namespace std;
class CTest
{
public:
int i;
CTest(){cout << "construct" << endl;}
CTest(CTest& obj){cout << "call copy construct" << endl;} //拷貝構造函數
~CTest(){cout << "discontruct" << endl;}
};
void test(CTest obj)
{
}
int main()
{
CTest testObj;
test(testObj);
return 0;
}
此時程序的輸出為:
construct
call copy construct
discontruct
discontruct
當用一個對象初始化另外一個對象時,也對調用拷貝構造函數。
如
CTest test1;
CTest test2 = test1; //調用test1的拷貝構造函數初始化對象test2
但是對於下面的表達式:
CTest test1,test2;
test2 = test1;
卻不會調用CTest的拷貝構造函數,因為test2已經被初始化過了,此時如果想要正確對test2賦值,需要重載運算符=
運算符重載的方式為 :
YourClass& operator=(YourClass& obj)
{
return *this;
}
雖然不編寫拷貝構造函數和重載運算符=,在大部分的情況下代碼都能正常工作,因為編譯器會生成一個默認的拷貝構造函數並且在對象賦值的時候也會作些特殊處理。但是我們不能完全相信系統始終能正常理解你的代碼。所以讓代碼完全在自己的控制之下才是一個好的方法。所以時刻不要忘記編寫拷貝構造函數和重載。