賦值運算子複製建構函式(拷貝建構函式)易混處總
阿新 • • 發佈:2019-02-20
賦值運算子和複製建構函式都是用已存在的B物件來建立另一個物件A。不同之處在於:賦值運算子處理兩個已有物件,即賦值前B應該是存在的;複製建構函式是生成一個全新的物件,即呼叫複製建構函式之前A不存在。
CTemp a(b); //複製建構函式,C++風格的初始化
CTemp a=b; //仍然是複製建構函式,不過這種風格只是為了與C相容,與上面的效果一樣,在這之前a不存在,或者說還未構造好。
CTemp a;
a=b; //賦值運算子
在這之前a已經通過預設建構函式構造完成。
例項總結:
重點:包含動態分配成員的類 應提供拷貝建構函式,並重載"="賦值操作符。
以下討論中將用到的例子:
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指向堆中分配的一段記憶體空間。
一、拷貝建構函式
呼叫拷貝建構函式1
int main(int argc, char* argv[])
{
CExample theObjone;
theObjone.Init(40);
//現在需要另一個物件,需要將他初始化稱物件一的狀態
CExample theObjtwo=theObjone;//拷貝建構函式
...
}
語句"CExample theObjtwo=theObjone;"用theObjone初始化theObjtwo。
其完成方式是記憶體拷貝,複製所有成員的值。
完成後,theObjtwo.pBuffer==theObjone.pBuffer。
即它們將指向同樣的地方(地址空間),指標雖然複製了,但所指向的空間內容並沒有複製,而是由兩個物件共用了。這樣不符合要求,物件之間不獨立了,併為空間的刪除帶來隱患。
所以需要採用必要的手段來避免此類情況。
回顧以下此語句的具體過程:通過拷貝建構函式(系統預設的)建立新物件theObjtwo,並沒有呼叫theObjtwo的建構函式(vs2005試驗過)。
可以在自定義的拷貝建構函式中新增輸出的語句測試。
注意:
對於含有在自由空間分配的成員時,要使用深度複製,不應使用淺複製。
呼叫拷貝建構函式2
當物件直接作為引數傳給函式時,函式將建立物件的臨時拷貝,這個拷貝過程也將調同拷貝建構函式。
例如
BOOL testfunc(CExample obj);
testfunc(theObjone); //物件直接作為引數。
BOOL testfunc(CExample obj)
{
//針對obj的操作實際上是針對複製後的臨時拷貝進行的
}
呼叫拷貝建構函式3
當函式中的區域性物件被被返回給函式調者時,也將建立此區域性物件的一個臨時拷貝,拷貝建構函式也將被呼叫
CTest func()
{
CTest theTest;
return theTest
}
二、賦值符的過載
下面的程式碼與上例相似
int main(int argc, char* argv[])
{
CExample theObjone;
theObjone.Init(40);
CExample theObjthree;
theObjthree.Init(60);
//現在需要一個物件賦值操作,被賦值物件的原內容被清除,並用右邊物件的內容填充。
theObjthree=theObjone;
return 0;
}
也用到了"="號,但與"一、"中的例子並不同,"一、"的例子中,"="在物件宣告語句中,表示初始化。更多時候,這種初始化也可用括號表示。
例如 CExample theObjone(theObjtwo);
而本例子中,"="表示賦值操作。將物件theObjone的內容複製到物件theObjthree;,這其中涉及到物件theObjthree原有內容的丟棄,新內容的複製。
但"="的預設操作只是將成員變數的值相應複製。舊的值被自然丟棄。
由於物件內包含指標,將造成不良後果:為了避免記憶體洩露,指標成員將釋放指標所指向的空間,以便接受新的指標值,這正是由賦值運算子的特徵所決定的。但如果是"x=x"即自己給自己賦值,會出現什麼情況呢?x將釋放分配給自己的記憶體,然後,從賦值運算子右邊指向的記憶體中複製值時,發現值不見了。
因此,包含動態分配成員的類除提供拷貝建構函式外,還應該考慮過載"="賦值操作符號。
類定義變為:
class CExample
{
...
CExample(const CExample&); //拷貝建構函式
CExample& operator = (const CExample&); //賦值符過載
...
};
//賦值操作符過載
CExample & CExample::operator = (const CExample& RightSides)
{
nSize=RightSides.nSize; //複製常規成員
char *temp=new char[nSize]; //複製指標指向的內容
memcpy(temp, RightSides.pBuffer, nSize*sizeof(char));
delete []pBuffer; //刪除原指標指向內容 (將刪除操作放在後面,避免X=X特殊情況下,內容的丟失)
pBuffer=temp; //建立新指向
return *this
}
三、拷貝建構函式使用賦值運算子過載的程式碼。
CExample::CExample(const CExample& RightSides)
{
pBuffer=NULL;
*this=RightSides //呼叫過載後的"="
}
CTemp a(b); //複製建構函式,C++風格的初始化
CTemp a=b; //仍然是複製建構函式,不過這種風格只是為了與C相容,與上面的效果一樣,在這之前a不存在,或者說還未構造好。
CTemp a;
a=b; //賦值運算子
在這之前a已經通過預設建構函式構造完成。
例項總結:
重點:包含動態分配成員的類 應提供拷貝建構函式,並重載"="賦值操作符。
以下討論中將用到的例子:
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指向堆中分配的一段記憶體空間。
一、拷貝建構函式
呼叫拷貝建構函式1
int main(int argc, char* argv[])
{
CExample theObjone;
theObjone.Init(40);
//現在需要另一個物件,需要將他初始化稱物件一的狀態
CExample theObjtwo=theObjone;//拷貝建構函式
...
}
語句"CExample theObjtwo=theObjone;"用theObjone初始化theObjtwo。
其完成方式是記憶體拷貝,複製所有成員的值。
完成後,theObjtwo.pBuffer==theObjone.pBuffer。
即它們將指向同樣的地方(地址空間),指標雖然複製了,但所指向的空間內容並沒有複製,而是由兩個物件共用了。這樣不符合要求,物件之間不獨立了,併為空間的刪除帶來隱患。
所以需要採用必要的手段來避免此類情況。
回顧以下此語句的具體過程:通過拷貝建構函式(系統預設的)建立新物件theObjtwo,並沒有呼叫theObjtwo的建構函式(vs2005試驗過)。
可以在自定義的拷貝建構函式中新增輸出的語句測試。
注意:
對於含有在自由空間分配的成員時,要使用深度複製,不應使用淺複製。
呼叫拷貝建構函式2
當物件直接作為引數傳給函式時,函式將建立物件的臨時拷貝,這個拷貝過程也將調同拷貝建構函式。
例如
BOOL testfunc(CExample obj);
testfunc(theObjone); //物件直接作為引數。
BOOL testfunc(CExample obj)
{
//針對obj的操作實際上是針對複製後的臨時拷貝進行的
}
呼叫拷貝建構函式3
當函式中的區域性物件被被返回給函式調者時,也將建立此區域性物件的一個臨時拷貝,拷貝建構函式也將被呼叫
CTest func()
{
CTest theTest;
return theTest
}
二、賦值符的過載
下面的程式碼與上例相似
int main(int argc, char* argv[])
{
CExample theObjone;
theObjone.Init(40);
CExample theObjthree;
theObjthree.Init(60);
//現在需要一個物件賦值操作,被賦值物件的原內容被清除,並用右邊物件的內容填充。
theObjthree=theObjone;
return 0;
}
也用到了"="號,但與"一、"中的例子並不同,"一、"的例子中,"="在物件宣告語句中,表示初始化。更多時候,這種初始化也可用括號表示。
例如 CExample theObjone(theObjtwo);
而本例子中,"="表示賦值操作。將物件theObjone的內容複製到物件theObjthree;,這其中涉及到物件theObjthree原有內容的丟棄,新內容的複製。
但"="的預設操作只是將成員變數的值相應複製。舊的值被自然丟棄。
由於物件內包含指標,將造成不良後果:為了避免記憶體洩露,指標成員將釋放指標所指向的空間,以便接受新的指標值,這正是由賦值運算子的特徵所決定的。但如果是"x=x"即自己給自己賦值,會出現什麼情況呢?x將釋放分配給自己的記憶體,然後,從賦值運算子右邊指向的記憶體中複製值時,發現值不見了。
因此,包含動態分配成員的類除提供拷貝建構函式外,還應該考慮過載"="賦值操作符號。
類定義變為:
class CExample
{
...
CExample(const CExample&); //拷貝建構函式
CExample& operator = (const CExample&); //賦值符過載
...
};
//賦值操作符過載
CExample & CExample::operator = (const CExample& RightSides)
{
nSize=RightSides.nSize; //複製常規成員
char *temp=new char[nSize]; //複製指標指向的內容
memcpy(temp, RightSides.pBuffer, nSize*sizeof(char));
delete []pBuffer; //刪除原指標指向內容 (將刪除操作放在後面,避免X=X特殊情況下,內容的丟失)
pBuffer=temp; //建立新指向
return *this
}
三、拷貝建構函式使用賦值運算子過載的程式碼。
CExample::CExample(const CExample& RightSides)
{
pBuffer=NULL;
*this=RightSides //呼叫過載後的"="
}