C++學習筆記----拷貝控制(拷貝構造、拷貝賦值、解構函式)
阿新 • • 發佈:2021-01-30
技術標籤:C++
文章目錄
1.合成拷貝成員
在我們的自定義的類中,如果我們沒有自定義拷貝建構函式、拷貝賦值函式、以及解構函式,編譯器將會為我們自動合成上述函式。
class copy_test
{
public:
void set(int val)
{
test = val;
}
void show()
{
cout << test << endl;
}
private:
int test;
};
int main()
{
copy_test c1;
c1.set(1);//c1的test設定為1
copy_test c2(c1);//呼叫合成的拷貝建構函式
copy_test c3 = c1;//呼叫合成的拷貝賦值函式
c1.show();
c2.show();
c3.show();
}
上述輸出結果均為1,說明編譯器去確實為我們生成了合成的拷貝構造和拷貝賦值運算子。但是對於編譯器自己合成的拷貝建構函式和拷貝賦值運算子,編譯器僅僅是採用簡單的賦值方式拷貝,如上的例子的合成拷貝建構函式可能是這樣的:
copy_test:: copy_test(const copy_test &c1)
{
test=c1.test;
}
2.自定義解構函式
解構函式會在物件即將被銷燬的時候進行呼叫,自定義的解構函式一般用於將函式函式內建的動態記憶體空間進行釋放。
但是對於編譯器合成的拷貝控制成員,可能會存在一些問題,如果類中的某個成員為指向動態資料型別的指標或者是一個引用時,當進行拷貝時,僅僅是通過指標的簡單賦值,那麼就會出現多個物件的內建指標指向同一片記憶體區域。當呼叫自定義的解構函式進行記憶體釋放時可能會出現執行同一記憶體的指標被多次釋放,這顯然是不合法的。
所以當一個類使用的是自定義的解構函式時,毫無疑問這個類應該使用自定義的拷貝建構函式和拷貝賦值運算子
上述案例可以按照如下所示改編:
class copy_test
{
public:
copy_test(int *p)
{
test = p;
}
copy_test(const copy_test &c)
{
test = new int(*(c.test));
}
copy_test& operator=(const copy_test &c)
{
test = new int(*(c.test));
return *this;
}
void show()
{
cout << *test << endl;
}
~copy_test()
{
delete test;
}
private:
int *test;
};
int main()
{
copy_test c1(new int(10));
copy_test c2(c1);
copy_test c3 = c1;
c1.show();
c2.show();
c3.show();
}
一個類的解構函式和賦值建構函式往往是成對出現的。
3.=default和=delete
如果我們希望編譯器為我們顯示的生成合成拷貝控制成員,我們可以使用 =default 來進行顯示說明。
我們只能對具有合成版本的成員函式使用=default即預設建構函式、拷貝建構函式、賦值運算子過載、解構函式。
有時候我們希望能夠阻止類的拷貝和賦值,如我們的IO類不允許賦值和拷貝,我們通過使用=delete來防止函式進行拷貝和賦值。
lass test
{
public:
test() = default;//使用預設建構函式
test(const test& t) = delete;//不使用拷貝建構函式
test& operator = (const test &t) = delete;//不使用合成賦值運算子
~test() = default;//使用合成的解構函式
};
=delete的使用應該具有以下規則:如果有一個類的資料成員不能預設構造、拷貝、複製或銷燬,則相應的成員函式將會被定義為刪除的。
- 解構函式不能是刪除的成員,否則物件將無法銷燬。
- 一個成員有刪除的或者不可訪問的解構函式則會導致合成的預設和拷貝建構函式被定義為刪除的。
- 具有引用或者const成員的類具有刪除的合成拷貝構造和合成複製函式,對一個const的成員複製是不允許的,對引用的複製,依然是同一個變數的引用,這沒有太大意義。