浅拷贝和深拷贝详解

浅拷贝

浅拷贝简介

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

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

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

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

#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
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
拷贝拷贝是在对象复制过程中常用的两种方式,它们的区别在于复制后的对象与原始对象之间的关系。 拷贝(Shallow Copy)是创建一个新对象,该对象与原始对象的元素是对原始对象中元素的引用。换言之,复制后的对象与原始对象共享内部对象的引用。当修改原始对象中的可变元素时,这些修改也会反映在复制后的对象中。 拷贝(Deep Copy)则是创建一个全新的独立对象,复制后的对象与原始对象完全独立,互不影响。拷贝会递归地复制原始对象及其内部所有可变对象,而不仅仅是引用。 举个例子来说明: ```python import copy # 原始对象 original_list = [1, 2, [3, 4]] # 拷贝 shallow_copy = copy.copy(original_list) # 拷贝 deep_copy = copy.deepcopy(original_list) # 修改原始对象中的可变元素 original_list[0] = 10 original_list[2].append(5) print(original_list) # 输出: [10, 2, [3, 4, 5]] print(shallow_copy) # 输出: [10, 2, [3, 4, 5]] print(deep_copy) # 输出: [1, 2, [3, 4]] ``` 在上面的例子中,原始对象是一个列表 `original_list`,其中包含一个整数和一个列表。通过拷贝 `copy.copy()` 和拷贝 `copy.deepcopy()` 分别创建了复制对象 `shallow_copy` 和 `deep_copy`。 当我们修改原始对象的第一个元素和第三个元素的子列表时,可以看到拷贝对象也受到了影响,而拷贝对象保持不变。 因此,在需要创建独立的对象副本且避免修改原始对象时,拷贝是更安全和可靠的选择,而拷贝则更适合在一些特定场景下节省内存或共享数据。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值