【C++】string类的深浅拷贝问题

浅拷贝问题

我们知道,定义一个类的时候,如果我们没有写构造,拷贝构造,赋值运算符重载,析构方法的话,编译器会自动生成。当该类成员中涉及到资源的管理时,实现的就是浅拷贝。所以,以上这几种方式是必须要程序猿手动实现的。

举例来看:
在这里插入图片描述
图中所示:实现了构造和析构,没有显式定义拷贝构造,这时先创建了一个s1,当使用拷贝构造创建s2时,由于没有显式定义,编译器自动生成的实际上是直接将s1中str的值直接复制给了s2中的str,那这样就导致两个指针指向了同一块内存空间。当test函数退出时,先释放s2,调用析构函数,可以成功将s2中str指向的内存空间释放掉,接着释放s1,这时调用析构函数进行释放时,s1中的str已经是野指针了,释放野指针就会报错

因此当前代码奔溃的根本原因就是浅拷贝

浅拷贝:也称位拷贝,编译器只是将对象中的值拷贝过来。如果对象中管理资源,最后就会导致多个对象共享同一份资源,当一个对象销毁时就会将该资源释放掉,而此时另一些对象不知道该资源已经被释放,以为还有效,所以当继续对资源进项操作时,就会发生发生了访问违规。

要想实现深拷贝,我们要从逻辑上知道如何做
在这里插入图片描述

每通过拷贝构造创建一个新的string对象的时候,都需要给新创建的对象开辟空间,将原对象中指向空间的内容拷贝到新空间,让原对象和新对象都拥有各自的空间,这样就可以实现深拷贝。

构造/析构函数

注意:在构造时需要进行资源管理,析构时需要进行资源释放

class String
{
public:
	String(const char* str = "")
	{
		if (str == nullptr)
			str = "";
		_str = new char[strlen(str) + 1];
		strcpy(_str, str);
	}
	~String()
	{
		if (_str)
		{
			delete[] _str;
			_str = nullptr;
		}
	}
private:
	char* _str;
};

拷贝构造/赋值运算符重载的多种写法

注意:进行拷贝构造/赋值运算符重载时,有多种写法,主要分为传统版和现代版,为了实现深拷贝,实际上要让新创建的string对象拥有各自的空间。

//传统版:代码易懂,但是代码重复率高
	String(const String& s)
		:_str(new char[strlen(s._str)+1])// 给当前对象的指针变量new一段空间,+1是为了存放'\0'
	{
	    // 将s中指向空间的内容拷贝到当前对象,当前对象就拥有了和s相同内容的空间,但是两份空间是独立的
		strcpy(_str, s._str);
	}
	String& operator=(const String& s)
	{
		//需要判断是否为自己给自己赋值
		if (this != &s)
		{
			//先创建一个临时指针,空间大小和s中指针指向的空间一致
			char* tmp = new char[strlen(s._str) + 1];
			//将其内容拷贝至tmp中
			strcpy(tmp, s._str);
			//释放旧空间
			delete[] _str;
			//将指针指向修改为新指向
			_str = tmp;
		}
		return *this;
	}

现代版的写法,省去了重复的new和strcpy,而是直接使用swap,交换两个指针的指向即可,临时对象在函数结束时调用析构函数释放资源,将临时对象原来指向的空间返回给需要空间的对象。

//现代版:代码简洁,但是不易理解
	String(const String& s)
		:_str(nullptr)//后续需要将tmp中指针指向改为nullptr,调用析构函数释放时就不做任何操作了
	{
		//用构造函数构造一个临时对象
		String tmp(s._str);
		//将当前对象中的指针指向和临时对象中指针指向交换,当前对象就拥有和s中指针指向相同内容的空间,
		//同时,tmp中指针的指向是nullptr,调用析构函数释放时也不需要做任何操作
		swap(_str, tmp._str);
	}
	// 传址方式的赋值运算符重载
	String& operator=(const String& s)
	{
		if (this != &s)
		{
			String tmp(s._str);
			//调用析构函数时,_str释放的是原来tmp的空间,tmp释放的是原来_str的旧空间
			swap(_str, tmp._str);
		}
		return *this;
	}
	// 传值方式的赋值运算符重载
	String& operator=(String s)
	{
		//以传值方式传递时,s是原来s的临时拷贝,可以直接通过交换的方式,
		//让_str获得临时s指针指向的内容,临时s释放的是原来_str的旧空间
		swap(_str, s._str);
		return *this;
	}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值