首先我们先记住一个结论:编译器自动生成的拷贝构造和赋值运算符是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;
}