【C++】深浅拷贝问题

首先来看一段代码:

#include <iostream>
#include <string.h>

using namespace std;

class String
{
public:
	string(const char* str = "wsc")
	{
		_str = (const char*)malloc(strlen(strlen) + 1);
		strcpy(_str, str);
	}
	~String()
	{
		cout << "~String()" << endl;
		free(_str);
	}
private:
	char* _str;
};

int main()
{ 
	String s1("hello");
	String s2(s1);
	system("pause");
	return 0;
}

这段代码本意是拷贝构造s1给s2,但是通过测试,我们发现程序会崩溃,那为什么会造成崩溃呢?这里涉及到一个深浅拷贝问题。下面来说说,什么是浅拷贝,什么又是深拷贝?

浅拷贝:按内存存储字节序完成的拷贝就叫做浅拷贝,又称值拷贝。在c++中,如果我们不写拷贝构造函数,系统自动生成的拷贝构造就是我们所说的值拷贝。

需要说明的是,我们在写日期类时,可以不写拷贝构造函数,使用编译器自动生成的函数,不会有什么问题,但是像String这种比较特殊的类,如果我们还是不写拷贝构造函数的话,程序就会出现上面代码中的问题,具体原因是:

这个程序会释放两次,但是正常情况下只能释放一次,开辟空间和释放空间必须是一一对应的,因为当释放掉一次后,此时这块空间已经不属于你,而属于操作系统,如果恰好这块空间被操作系统分配给别人,如果再释放一次的话,相当于把别人的空间给释放了。这是不允许的,下面画图解释一下:

深拷贝:如果一个类中涉及到资源的管理,那么其拷贝构造函数、赋值运算重载、析构函数都要显式给出,也就是我们要自己实现,且都是按照深拷贝方式来完成。

深拷贝的原理:自己开辟一块空间,把s2的数据拷贝过去,析构时,自己析构自己的,也不会出现多次释放的问题。

传统的(深)拷贝构造:

String(const String& s)//传统的深拷贝
	{
		_str = new char[strlen(s._str) + 1];
		strcpy(_str, s._str);
	}

传统的赋值重载:先释放掉s3的空间,再去开辟一块和s2一样大小的空间

String& operator=(const String& s)//传统的赋值
	{
		if (*this != &s)//防止自己给自己赋值
		{
			delete[] _str;
			_str = new char[strlen(s._str) + 1];
			strcpy(_str, s._str);
		}
		return *this;
	}

现代的(深)拷贝构造:

String(const String& s)
		//s3(s2)
		:_str(nullptr)//s3一开始是随机值,才准备构造,所以把要构造的s3初始化为nullptr
	{
		String tmp(s._str);//构造一个和s2一样的tmp
		swap(_str, tmp._str);//把tmp和已经初始化为nullptr的s3一交换,拷贝构造完成
	}

现代的赋值重载:

String& operator=(const String& s)
	{
		if (this != &s)
		{
			String tmp(s._str);
			swap(_str, tmp._str);
		}
		return *this;
	}

还有一种更简单的:s3(s2)

String& operator=(String s)
	{
		swap(_str, s._str);
		return *this;
	}

传统的拷贝构造:比较直观

现代写法:简洁+复用

其实,解决浅拷贝还有另外一种方式,就是写时拷贝,写时拷贝实质上就是拖延症,它采用引用计数,当count减到0时,意味着没有一个对象指向这块空间,当count>1时,不释放,让count--,直到count为1时,表明只有一个对象指向这块空间,此时再去释放这块空间,虽然这种方式效率高,但是会存在线程安全问题,所以我们还是不推荐这种方式。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值