複製初始化
以下討論中將用到的例子:
class CExample
{
public:
CExample(){pBuffer=NULL; nSize=0;}
~CExample(){delete []pBuffer;}
void Init(int n){ pBuffer=new char[n]; nSize=n;}
private:
char *pBuffer; //類的對象中包含
指針,指向動態分配的內存資源
int nSize;
};
這個類的主要特點是包含指向其他資源的
指針,pBuffer指向堆中動態分配的一段內存空間。
int main(int argc, char* argv[])
{
CExample theObjone;
theObjone.Init(40);
//現在需要另一個對象,並將它初始化爲theObjone
CExample theObjtwo=theObjone;
...
}
語句"CExample theObjtwo=theObjone;"用theObjone初始化theObjtwo。
回顧一下此語句的具體過程:首先建立對象theObjtwo,並調用其構造函數,然後成員被複制初始化。
其完成方式是內存拷貝,複製所有成員的值。完成後,theObjtwo.pBuffer==theObjone.pBuffer。
即它們將指向同樣的地方,
指針雖然複製了,但所指向的空間並沒有複製,而是由兩個對象共用了。這樣不符合要求,對象之間不獨立了,併爲空間的刪除帶來隱患。所以需要採用必要的手段來避免此類情況:可以在構造函數中添加操作來解決
指針成員的這種問題。
所以C++語法中除了提供缺省形式的構造函數外,還規範了
另一種特殊的構造函數:拷貝構造函數,一種特殊的
構造函數重載。上面的語句中,如果類中定義了拷貝構造函數,在對象
複製初始化時,調用的將是拷貝構造函數,而不是
缺省構造函數。在拷貝構造函數中,可以根據傳入的
變量,複製
指針所指向的資源。
拷貝構造函數的格式爲:類名(const 類名& 對象名);//拷貝構造函數的原型,參數是
常量對象的引用。由於拷貝構造函數的目的是成員複製,不應修改原對象,所以
建議使用const關鍵字。
提供了拷貝構造函數後的CExample類定義爲:
class CExample
{
public:
CExample(){pBuffer=NULL; nSize=0;}
~CExample(){delete []pBuffer;}
CExample(const CExample&); //拷貝構造函數
void Init(int n){ pBuffer=new char[n]; nSize=n;}
private:
char *pBuffer; //類的對象中包含
指針,指向動態分配的內存資源
int nSize;
};
CExample::CExample(const CExample& RightSides) //拷貝構造函數的定義
{
nSize=RightSides.nSize; //複製常規成員
pBuffer=new char[nSize]; //複製
指針指向的內容
memcpy(pBuffer,RightSides.pBuffer,nSize*sizeof(char));
}
這樣,定義新對象,並用已有對象初始化新對象時,CExample(const CExample& RightSides)將被調用,而已有對象用別名RightSides傳給構造函數,以用來作複製。
對象按值傳遞
下面介紹拷貝構造函數的另一種調用:當對象直接作爲參數傳給函數時,函數將建立對象的臨時拷貝,這個拷貝過程也將調用拷貝構造函數。例如:
BOOL testfunc(CExample obj);
testfunc(theObjone); //對象直接作爲參數。
BOOL testfunc(CExample obj)
{
//針對obj的操作實際上是針對複製後的臨時拷貝進行的
}
還有一種情況,也是與臨時對象有關:當函數中的局部對象作爲返回值被返回給函數調者時,也將建立此局部對象的一個臨時拷貝,拷貝構造函數也將被調用。
CTest func()
{
CTest theTest;
return theTest;
}
總結:當某對象是按值傳遞時(無論是作爲函數參數,還是作爲函數返回值),編譯器都會先建立
一個此對象的臨時拷貝,而在建立該臨時拷貝時就會調用類的拷貝構造函數。