浅拷贝和深拷贝详解

C++ 专栏收录该内容
1 篇文章 0 订阅

浅拷贝

浅拷贝简介

浅拷贝是按位拷贝对象,它会创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值;如果属性是内存地址(引用类型),拷贝的就是内存地址 ,因此如果其中一个对象改变了这个地址,就会影响到另一个对象。即默认拷贝构造函数只是对对象进行浅拷贝复制(逐个成员依次拷贝),即只复制对象空间而不复制资源。

一般情况下,只需使用系统提供的浅拷贝构造函数即可,但是,如果对象的数据成员包括指向堆空间的指针,就不能使用这种拷贝方式,因为两个对象都拥有同一个资源,对象析构时,该资源将经历两次资源返还,此时必须自定义深拷贝构造函数,为创建的对象分配堆空间,否则会出现动态分配的指针变量悬空的情况。

部分情况下浅拷贝所带来的问题

首先看下面这段代码可以正常运行。

#include<iostream>
#include<string.h>
#include<stdlib.h>
using namespace std;

class String
{
public:
	String(const char *str = "")
	{
		m_data = (char*)malloc(strlen(str) + 1);
		strcpy(m_data, str);
	}
	char* GetString()const
	{
		return m_data;
	}
	~String()
	{
		free(m_data);
		m_data = NULL;
	}
private:
	char* m_data;
};

int main()
{
	String s1("abc");
	cout << s1.GetString() << endl;
}

此代码可以正常运行,下图为运行结果
在这里插入图片描述

然后我们在主函数中建立s2,并通过s1进行拷贝构造

int main()
{
	String s1("abc");
	cout << s1.GetString() << endl;

	String s2 = s1;
}

在运行后发现程序崩溃

原因:
在实例化s1时程序正常执行,而当用s1拷贝构造s2时,相当于s1和s2指向了同一块空间,如下图:
在这里插入图片描述
打开监视窗口,如下图,我们看到s1和s2的m_data都指向了0x013b1e60的位置,也就是说同一块内存被两个指针所指。在这里插入图片描述
程序运行到这里都没有问题,但是当主函数结束,调用析构函数时,先通过s2对象将m_data所指向的空间释放,再释放s1,而此时s1对象的m_data还指向刚刚已经被释放过的空间,也就是说这个空间被释放了两次,所以程序会崩溃。

通过这个例子我们会发现,默认的拷贝构造函数是无法满足我们所有的需求的,这时我们需要自己编写拷贝构造函数。

深拷贝

深拷贝简介

在进行赋值之前,为指针类型的数据成员另辟了一个独立的内存空间,实现真正内容上的拷贝。这种拷贝称为深拷贝。

深拷贝操作

我们希望在拷贝构造时不能简单地将s2中m_data的指针指向s1中m_data所指向的地址,而是要再申请一个跟它一样大小的空间,并且空间里的值也要相同,再让s2中的m_data指向新申请的空间。如下图:
在这里插入图片描述
编写拷贝构造函数代码如下:

String(const String &s)
	{
		m_data = (char*)malloc(strlen(s.m_data) + 1);//申请空间
		strcpy(m_data, s.m_data);//将数据拷贝
	}

在我们进行深拷贝后,程序正常执行,打开监视窗口,我们也可以看到两个对象的m_data所指向的空间并不一样,表明深拷贝操作成功。
在这里插入图片描述

总结:

如果一个类拥有指针类型的成员变量,那么绝大部分情况下就需要深拷贝,因为只有这样,才能将指针指向的内容再复制出一份来,让原有对象和新生对象相互独立,彼此之间不受影响。如果类的成员变量没有指针,一般浅拷贝足以。

  • 5
    点赞
  • 0
    评论
  • 2
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

相关推荐
©️2020 CSDN 皮肤主题: 游动-白 设计师:白松林 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值