C++學習筆記----拷貝控制和資源管理
技術標籤:C++
在實現拷貝建構函式的複製運算子過載時,我們需要注意以下幾種情況:
1.作為值的拷貝
所謂的值的拷貝就是說每次拷貝的是一個獨立的物件,拷貝物件和被拷貝的物件之間相互獨立,這種拷貝每次都要重新開闢空間,這樣才能使得兩者之間獨立。但是對於過載操作符而言,我們需要先將需要拷貝的物件臨時儲存起來,然後釋放複製運算子左邊的記憶體空間,最後才將臨時變數拷貝到等號左邊的物件中。這樣可以防止自複製而產生異常。
2.指標值的拷貝。
所謂指標值的拷貝就是拷貝物件和源物件共享相同的地址空間中的值。這種情況下需要引入一個計數器來記錄指向記憶體空間的指標的數量。在進行拷貝和賦值的時候需要傳遞技術器的指標,以便各個物件共享一個計數器。這種情況下,我們使用解構函式進行釋放記憶體空間時我們只能應該首先判斷計數器的值是否為零,如果是的話就釋放記憶體,否則僅僅計數器減一即可。在過載賦值運算子時一定要留意自賦值產生異常的情況,我們應該先將賦值運算子右側的計數器的值加一後在進行賦值操作,在等號左邊的計數器減一的時候應該判斷計數器是否為零,如果計數器為零則應該釋放物件佔用的空間,左後在進行拷貝。
3.交換操作
標準庫函式中的交換函式交換兩個物件時總是會拷貝一個臨時的物件進行交換,這就會造成空間和時間的浪費,因此一般自定義基於指標的交換將會提高交換的效能。案例:
class test
{
public:
friend void swap(test &t1,test &t2);
private:
string* p;
int i;
};
void swap(test& t1, test& t2)
{
using std::swap;
swap(t1.p,t2.p);
swap(t1.i,t2.i) ;
}
我們自定義的swap函式中呼叫標準庫函式中的swap函式交換test類中的p指標和變數i,因此並不用建立一個新的臨時變數的test類的物件來進行交換,採用這種方式可以具有更高的效能。
假如有一個Foo類,該類包含一個test類的物件,則在進行Foo類的物件的交換的時候將會呼叫test類的為引數的swap函式,而不是便準庫函式。
class Foo
{
public:
friend void swap(Foo &f1,Foo &f2);
private:
test t;//test類的物件
};
//test類的物件版本的交換函式
void swap(test& t1, test& t2)
{
using std::swap;
swap(t1.p,t2.p);
swap(t1.i,t2.i);
cout << "swap for test" << endl;
}
//Foo類物件版本的交換函式
void swap(Foo &f1,Foo &f2)
{
using std::swap;
swap(f1.t, f2.t);//呼叫test類版本的swap函式
cout << "swap for Foo" << endl;
}
int main()
{
Foo f1, f2;
swap(f1,f2);
}
上述案例的輸出如圖:
顯然呼叫了test類版本的swap函式,因為傳入的引數是test類的物件,顯然test版本的swap函式比庫函式更匹配,這樣交換的效率就會更加的高。
3.swap函式用於賦值運算子
用swap函式來實現運算子過載,被稱為是拷貝並交換的技術。假設上述test的賦值運算子過載採用拷貝並交換 的技術過載賦值運算子。
test& test::operator=(test t)
{
swap(*this,t);
return *this;
}
這個版本的賦值運算子中,引數並不是一個引用,當我們進行傳參的時候,函式的區域性區域內將會對傳入的test物件的引數進行一個拷貝,當函式呼叫結束後,這個區域性變數指向的記憶體區域將會被釋放。所以進行我們把傳入的引數的拷貝的和this所指向的記憶體區域進行交換(指標交換),然後返回this。當this返回後,傳入t指向的區域將會被釋放(此時t指向的為this原來指向的地址)。
採用拷貝和交換的技術的賦值運算子是異常安全的,且能正確處理自賦值的問題。