首先我們先記住一個結論:編譯器自動生成的拷貝構造和賦值運算符是memcpy的一個過程。
實例過程以結構體代替,在c++中struct和class除了默認訪問權限基本等同。
struct TestStruct1
{
int arr[3];
int a;
};
int main()
{
TestStruct1 oneVar;
oneVar.arr[0]=1;
oneVar.arr[1]=2;
oneVar.arr[2]=3;
oneVar.a=4;
TestStruct1 twoVar=oneVar;
}
拷貝構造直接將oneVar的內存空間拷貝到twoVar的內存空間中。
考慮一個成員指針的情況:
struct TestStruct2
{
int arr[3];
int *a;
};
int main()
{
TestStruct1 oneVar;
oneVar.arr[0] = 1;
oneVar.arr[1] = 2;
oneVar.arr[2] = 3;
oneVar.a = new int(4);
TestStruct1 twoVar = oneVar;
}
由此看出oneVar和twoVar的指針成員共享了oneVar.a的堆內存空間,如果oneVar和twoVar其中一個執行了delete oneVar.a或者
delete twoVar.a(4這塊內存被回收了),這個時候另外一個變量再次從a取值就是產生未定義的行爲。
爲了解決上述共享堆內存而引發的未定義行爲問題,引入了深拷貝
struct TestStruct3
{
int arr[3];
int *a;
TestStruct3(){}
TestStruct3(const TestStruct3& other)
{
this->arr[0]=other.arr[0];
this->arr[1]=other.arr[1];
this->arr[2]=other.arr[2];
this->a = new int(*(other.a)); // 深拷貝解決的問題
}
~TestStruct3()
{
delete a; // 顯式釋放堆內存,否則默認的析構函數不會釋放對內存
a = nullptr;
}
};
int main()
{
TestStruct3 oneVar;
oneVar.arr[0]=1;
oneVar.arr[1]=2;
oneVar.arr[2]=3;
oneVar.a = new int(4);
TestStruct3 twoVar = oneVar;
}