【C++基礎】----操作符過載(03)
阿新 • • 發佈:2019-02-03
今天我們來討論操作符過載中比較重要的一個內容:賦值操作符的過載
1. 常量不允許出現在=左邊
由於編譯期對賦值有著嚴格的要求和限制,因此強制 operator=() 為成員函式。
成員函式的優點在於在呼叫時,永遠有一個隱式的this指標被呼叫,而反觀友元函式,我們可以將任何兩個物件傳給友元函式,在下面的例子中,我們會看到這將是一個很大的隱患。
如果我們使用了友元函式進行過載,那很有可能出現諸如
2 = 5 ;
之類的瘋狂語句,強制規定的根本原因是在友元函式的情況下使用者就可以打破C++的規定,使常量出現在等號的左邊。
2. 如何禁止自賦值
建立一個operator=()時,必須從右側物件中拷貝所有需要的資訊到當前物件,已完成物件的賦值。
#include <iostream> using namespace std ; class Value{ int a_ , b_ ; float c_ ; public : Value(int a , int b , float c) : a_(a) , b_(b) , c_(c) {} Value() : a_(0) , b_(0) , c_(0.0) {} Value operator=(const Value& obj) { a_ = obj.a_ ; b_ = obj.b_ ; c_ = obj.c_ ; return *this ; } friend ostream& operator<<(ostream& os , const Value& obj) ; }; ostream& operator<<(ostream& os , const Value& obj) { return os << "a=" << obj.a_ << ", b=" << obj.b_ << ", c=" << obj.c_ << endl ; } int main(void) { Value a , b(1 , 2 , 3.1) ; a = b ; cout << a ; return 0 ; }
其中有一個十分隱蔽的錯誤,就是自賦值的問題,我們有可能讓一個物件呼叫operator= 時傳進的引數也是自己,這樣實際上就會自己給自己賦值。在C++中這是一個很嚴重的錯誤,有可能出現在程式結束呼叫解構函式時,同一塊記憶體空間被釋放兩次的可怕後果。
因此,應記住一個常識:當我們準備給兩個相同型別的物件賦值時,先檢查這個物件是否在對自己進行賦值,如果我們不進行檢查,就可能產生難以發現的錯誤。
經過修改的過載函式應該這樣去實現
3. 類中的指標class Value{ int a_ , b_ ; float c_ ; public : Value(int a , int b , float c) : a_(a) , b_(b) , c_(c) {} Value() : a_(0) , b_(0) , c_(0.0) {} Value operator=(const Value& obj) { if(& obj == this) //if there two objects are identity return *this ; //we should shutdown this function a_ = obj.a_ ; b_ = obj.b_ ; c_ = obj.c_ ; return *this ; } friend ostream& operator<<(ostream& os , const Value& obj) ; };
如果物件中包含指向其他物件的指標,問題會更加複雜。
這就涉及了深拷貝和淺拷貝的區別。
因為我們如果僅僅使用之前一直介紹的淺拷貝的話,在賦值過程中,A物件指標成員所指向的物件的地址會原封不動的賦值給B物件,兩個物件會共同指向同一物件
如果我們使用上面的類,就會出現問題
為了方便起見,畫圖給大家描述一下
如果只是用簡單的淺拷貝,就會使得它們指向同一物件。
因此,我們在實現時,在指標的賦值時,不應該只是簡單的賦值,應該建立一個與原物件指標指向物件相同的新物件,再用被賦值物件的指標去指向,才能避免。
class CA
{
public:
char* p;
CA(){p = NULL;};
void Set(char* pStr)
{
delete []p;
if(pStr == NULL)
{
p = NULL;
}
else
{
p = new char[strlen(pStr)+1];
strcpy(p, pStr);
}
};
CA& operator=(CA& a)
{
cout<<” operator = invoked/n”<<endl;
//沒有檢測自賦值情況
delete []p;
p = a.p;
a.p = NULL;
return *this;
};
~CA(){delete []p;};
};
CA物件“擁有”它成員p指向的記憶體。所以,在賦值函式中,引數a將放棄 它的“擁有權”,並將它轉交給呼叫物件。(C++標誌庫中定義的智慧指標auto_ptr就是一種“擁有”型智慧指標,它也存在這種“擁有權轉移”的性質)
請見下面的例子程式碼(例子程式碼1):
CA a1, a2;
a1.Set(“Ok”);
a2 = a1;
我們的函式看起來工作的很好,但是,請看下面一條語句:
a2 = a2;// 悲劇發生了,a2“擁有”的記憶體被釋放了!
所以,賦值運算子函式應寫為下面的形式:
CA& CA::operator=(CA& a)
{
cout<<” operator = invoked/n”<<endl;
//檢測自賦值情況
if(this != &a)
{
delete []p;
p = a.p;
a.p = NULL;
}
return *this;
};