1. 程式人生 > >C++運算子過載(3)

C++運算子過載(3)

答案與拷貝建構函式一樣。如果一個類不包含指標,則不需要自定義賦值操作符與拷貝建構函式。編譯器會給每個類建立一個預設的拷貝建構函式和預設的賦值操作符。但是,當類中包含有指標或任何執行時分配的資源時,編譯器生成的這兩個函式,可能會失效。 參考下面程式:
#include<iostream>
using namespace std;
 
// 沒有自定義賦值操作符
class Test
{
    int *ptr;
public:
    Test (int i = 0)        { ptr = new int(i); }
    void setValue (int i) { *ptr = i; }
    void print()             { cout << *ptr << endl; }
};
 
int main()
{
    Test t1(5);
    Test t2;
    t2 = t1;
    t1.setValue(10);
    t2.print();
    return 0;
}
上面程式輸出結果為: 10

仔細看下main函式,會發現使用setValue()修改了t1的值後,物件t2也跟著改變了。這會導致未知的行為。
因為上面程式沒有自定義賦值操作符,所以編譯器生成了一個預設的,會將'ptr‘從右側拷貝到左側。所以兩個物件中的ptr指向相同位置。
可以使用兩種方法來解決上面問題:
1) 禁止物件之間進行賦值操作。我們可以使用自定義的賦值運算子並定義為private.
2) 自定義賦值運算子來實現深拷貝(deep copy).
對於拷貝建構函式,也是同樣的道理。

下面是過載了賦值運算子後的程式:

#include<iostream>
using namespace std;
 
class Test
{
    int *ptr;
public:
    Test (int i = 0)      { ptr = new int(i); }
    void setValue (int i) { *ptr = i; }
    void print()          { cout << *ptr << endl; }
    Test & operator = (const Test &t);
};
 
Test & Test::operator = (const Test &t)
{
   //檢測是否為自己
   if(this != &t)
     *ptr = *(t.ptr);
 
   return *this;
}

int main()
{
    Test t1(5);
    Test t2;
    t2 = t1;
    t1.setValue(10);
    t2.print();
    return 0;
}
執行結果:5

我們應該同時給上面程式加入拷貝建構函式,執行類似這樣的語句時“Test t3 = t4;” 不會出現未知行為。

注意賦值操作符中的if條件判斷。當過載賦值操作符時,必須要檢查自我賦值。否則,一個物件賦值給它自己,可能會導致未知的行為。在上面程式中,不一定需要自我賦值檢查,因為ptr總是指向一個整數,可以重用相同記憶體。但是總體來說,檢查自我賦值是一個好的程式設計實踐方法。