1、C++中重載賦值操作函數應該返回什么?
類重載賦值操作符一般都是作為成員函數而存在的,那函數應該返回什么類型呢?參考內置類型的賦值操作,例如
int x,y,z;
x=y=z=15;
賦值行為相當於x=(y=(z=15)),也就是賦值操作應該返回左操作數的引用,因此,為了和內置類型兼容,類中重載賦值操作符應該返回左操作數的引用,即*this,如下類A的重載賦值操作函數的聲明,
class A{};
A& A::operator=(const A&);
2、確保重載賦值操作具有良好的行為
對於賦值操作,首先應該想到的是怎么處理自我賦值,當類包含指針類型的數據時尤為重要,如下所示
class MyString{
public:
...
MyString(char *p=NULL);
MyString& operator=(const MyString&);
private:
char *str;
};
MyString a("hello");
MyString b("world");
a=a;
我們知道,進行賦值時,首先要釋放左操作數的資源,然后再根據右操作數對左操作數進行賦值,賦值操作函數如下所示
MyString& MyString::operator=(const MyString& rhs){
delete [] str;
str=new char[strlen(rhs.str)+1];
strcpy(str,rhs.str);
return *this;
}
當運行a=a時,會產生意向不到的后果,我們在函數中先釋放了左操作數的資源,然后訪問右操作數的資源,然后當自我賦值發生時,this==&rhs,也就是說我們先釋放了資源,然后又訪問了已經被釋放的資源的內容,這顯然會引起程序崩潰,所以我們需要進行是否是自我賦值操作的驗證,修改過后的函數如下
MyString& MyString::operator=(const MyString& rhs){
if(this==&rhs)
return *this;
delete [] str;
str=new char[strlen(rhs.str)+1];
strcpy(str,rhs.str);
return *this;
}
此時,函數雖然處理了自我賦值,但是仍然存在問題,如果new操作失敗了怎么辦?
我們已經釋放了左操作數的資源,但是在重新分配資源時由於空間不夠等原因失敗了,如果我們繼續對已經釋放了的資源進行訪問,會產生未定義的結果,賦值操作就不具備異常安全性,對於這個問題有兩種解決方案
第一,調整語句順序,先保存原來的資源,等重新分配資源完成以后再釋放以前的資源
MyString& MyString::operator=(const MyString& rhs){
char *pTemp=str;
str=new char[strlen(rhs.str)+1];
strcpy(str,rhs.str);
delete [] pTemp;
return *this;
}
這段代碼也能夠處理自我賦值,但是必須執行完函數中的所有復制、分配和釋放操作,如果自我賦值發生的概率比較高,我們也可以將測同語句放進該函數。
第二、采用copy and swap技術
MyString& MyString::operator=(const MyString& rhs){
if(this!=&rhs){
MyString temp(rhs);
char *pTemp=str;
str=temp.str;
temp.str=pTemp;
}
return *this;
}
這種技術賦值的是指針而不是為對象重新分配資源,我們在if語句內重新構造了一個新的臨時對象,然后將臨時對象的str和本類對象的str進行交換,當程序執行到if語句外時,臨時對象自動調用析構函數,釋放自己的資源,此時臨時對象所持有的資源就是原來this所持有的資源,該資源得以釋放,而現在this所持有的資源是rhs所持有的資源,即此時rhs和this中的str所指向的是同一塊空間。
3、復制對象時切勿忘記復制其每一個元素
對於有繼承關系的類,在對其派生類編寫復制控制的函數時,由於派生類無法訪問基類中的私有成員,所以在派生類的重載操作符函數中,需要首先調用其基類的賦值操作符函數對派生類中的基類成員進行賦值,然后再對派生類中的特有成員進行賦值。