一、什么是深拷贝和浅拷贝
简单的来说,【浅拷贝】是增加了一个指针,指向原来已经存在的内存。而【深拷贝】是增加了一个指针,并新开辟了一块空间让指针指向这块新开辟的空间。浅拷贝只是增加了指向同一个空间的对象。而深拷贝则是新建了全新的对象,它具有自己的空间。对于一个空类,编译器默认生成四个成员函数:默认构造函数、析构函数、拷贝构造函数、赋值函数。而直立棉的拷贝构造函数是浅拷贝。所以这时候如果类中含有指针变量,默认的拷贝构造函数必定出错。
二、浅拷贝
这里给出一个浅拷贝的例子。
#include <iostream>
#include <string>
using namespace std;
class String
{
public:
// 构造函数
String(const char* pStr = "")
{
if (NULL == pStr)
{
pstr = new char[1];
*pstr = '\0';
}
else
{
pstr = new char[strlen(pStr) + 1]; // 加1, 某位是'\0',结束符
strcpy(pstr, pStr);
}
}
// 拷贝构造函数
String(const String& s)
{
pstr = s.pstr; // 浅拷贝的问题,指向同一块空间,可能造成释放的错误 ,这是浅拷贝的缺点
}
// 赋值运算符重载
String& operator = (const String& s)
{
if (this != &s)
{
delete[] pstr; // 将原来所指向的空间释放
pstr = s.pstr; // 让pstr重新指向s的pstr所指向的空间(也会导致错误)
}
return *this;
}
~String()
{
// 2. 插入断点
if (NULL != pstr)
{
delete[] pstr;//释放指针所指向的内容
pstr = NULL;//将指针置为空
}
}
friend ostream& operator<<(ostream& _cout, const String& s)
{
_cout << s.pstr;
return _cout;
}
private:
char* pstr;
};
int main()
{
// 1. 插入断点
String s1("sss");
String s2(s1);
String s3(NULL);
s3 = s1;
cout << s1 << endl;
cout << s2 << endl;
cout << s3 << endl;
return 0;
}
这里我们将三个对象添加到监视里,发现他们的地址都是一样。
在析构函数处插入断点,析构s3时,将空间释放掉了,可以析构s2的时候报错了。因为它地址所处的空间就是s3的空间,以及被释放掉了,所以报错了。
三、深拷贝
深拷贝和浅拷贝的不同之处,仅仅在于修改了下【拷贝构造函数】,以及【赋值运算符的重载】。其实是给每个新建的对象分配了空间。
// 拷贝构造函数
String(const String& s)
{
pstr = new char[strlen(s.pstr) + 1];
strcpy(pstr, s.pstr);
}
// 赋值运算符重载
String& operator = (const String& s)
{
if (this != &s)
{
char* tmp = new char[strlen(s.pstr) + 1];//动态开辟一个临时变量,然后将pstr指向这一个新的临时变量里
delete[] pstr;//将原来的空间进行释放
strcpy(tmp, s.pstr);//将s.pstr里的内容复制到临时变量中
pstr = tmp;//pstr指向临时变量的这段空间
}
return *this;
}
现在可以看出三个对象的地址都不一样了,程序运行没有问题了。
这边可以简化一下深拷贝的【拷贝构造函数】,【赋值运算符重载】
// 拷贝构造函数
String(const String& s)
{
// 调用构造函数
String temp(s.pstr);
swap(pstr, temp.pstr);
}
// 赋值运算符重载
String& operator = (const String& s)
{
if (this != &s)
{
// 调用构造函数
String temp(s.pstr);
swap(pstr, temp.pstr);
}
return *this;
}
四、总结
- 浅拷贝只是增加了一个指针,指向已存在对象的内存。
- 浅拷贝在多个对象指向一块空间的时候,释放一个空间会导致其他对象所使用的空间也被释放了,再次释放便会出现错误
- 深拷贝是增加了一个指针,并新开辟了一块空间,让指针指向这块新开辟的空间。