图文并茂,一分钟看懂的C++深浅拷贝

对于深浅拷贝的问题,我就用C++中STL中string类的拷贝构造来解释一下。

简单来看,深浅拷贝就是看问题是不是只考虑了其中的一个方面,是不是很片面
在这里插入图片描述
浅拷贝的判断结果就相当于背影杀手的背面,深拷贝的判断结果就相当于回过头的正面。

浅拷贝(背影杀手)

我们知道C++与C的区别之一就是C++有了引用的概念,通过引用,我们就可以给变量起一个别名,然后更快速,更舒服的使用这个变量。

对于每一个类,都有一个默认的拷贝构造的成员函数。

  • 当我们的成员变量都是不含指针,数组,只是一个普通变量时

在这里插入图片描述

  • 当我们实现的是一个string 类的时候,成员变量中存储数据的就是一个指针类型的数组

在这里插入图片描述

分析出错的原因

我们在定义了一个指针类型的成员变量,就要在类中写一个析构函数(因为默认的析构函数是不会对成员变量做处理的),在析构函数中,我们把指针类型的空间释放,并且置空,好像没有什么问题,那为什么会发生错误呢?
在这里插入图片描述
仔细一看,我们的main函数在退出的时候,由于栈后进先出的特性,先释放的是我们浅拷贝的string 类的内存,调用析构函数后,两个指针所指向的内存空间被清空了,指针变成了空指针。
但是我们的函数栈帧中还有一个str指针没有释放,于是在释放的时候就变成了访问空指针,就会报错了。

究其本源还是我们在拷贝构造中,用的是浅拷贝,虽然节省了空间,但是却埋下了一个BUG的种子

但是我们使用C++STL中的string类时却不会出现这样的问题,这是为什么呢?说明string类中用的不是浅拷贝,而是深拷贝

深拷贝

深拷贝,顾明思议就是思考问题比较全面。
所以在深拷贝中,选择开辟一段和需要拷贝的变量一样大小的内存空间,然后按照按照顺序一个一个拷贝过去。

简而言之,就是创造一个新的变量,这个变量和需要拷贝的对象一模一样,只是内存空间不同。

程序

	string(const char* str)
	{
		//深拷贝
		int len = strlen(str);
		_str = new char[len + 1];//多开一个,预留一个 '\0'的空间
		strcpy(_str, str);
	}

运行结果

在这里插入图片描述
在深拷贝的时候,我们让两个指针变量指向不同的空间,但是他们的内容确实相同的。那么在释放内存的时候,也是自己释放自己的,不会出现冲突的情况。
在这里插入图片描述

深拷贝的另一种写法

拷贝构造

	//拷贝构造
	string(const string& s)
		:_str(nullptr)
	{
		string tmp(s._str);
		swap(_str, tmp._str);
	}

在这里插入图片描述

赋值运算符重载

	//赋值运算符重载
	string& operator=(const string& s)
	{
		if(this != &s)
		{
			string tmp(s._str);
			swap(_str, tmp._str);
		}
		return *this;	
	}

在这里插入图片描述

优化的写法

	//赋值运算符重载
	string& operator=(string s)
	{
		swap(_str, s._str);
		return *this;	
	}

这样写呢,不管等号右边是哪一个类,在调用=运算符重载的时候,都是先拷贝构造一个类作为形参。
然后再交换形参的_str 和右边类的_str。

这样写是不会出现内存泄漏的,因为形参是一个局部变量,局部变量在函数结束的时候,就会被释放。这里因为这是一个自定义的类,就会调用类的析构函数,来清理原字符串的空间。
也就是把原本需要手动书写的代码交给了析构函数来完成。

总结

  • 浅拷贝

浅拷贝可以使用的范围就是像日期类那样的,成员变量中没有指针或者数组类型的变量的时候,简简单单使用默认的构造函数就行了。

  • 深拷贝

深拷贝可以使用的就是所有成员变量了,因为他就是重新开辟一段空间,让两个变量虽然内存地址不相等,但是内存中指向的内容确是一样的。

在这里插入图片描述
你看懂了吗

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值