C++(深拷贝浅拷贝)详解

本文详细解释了C++中的深拷贝与浅拷贝的概念及其区别。通过实例演示了浅拷贝导致的问题,并展示了如何通过深拷贝解决这些问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

文章开始给大家瞧瞧昨晚拍的大黄

嘿嘿~

 捞捞之前的总结:十万字带你学习C++(细节拉满)​​​​​​

 fork函数详解_九九丸io的博客

  

什么是浅拷贝?

在未定义显示拷贝构造函数的情况下,系统会调用默认的拷贝函数——即浅拷贝,它能够完成成员的一一复制,按位拷贝。
即浅拷贝:简单的赋值拷贝操作(这个是系统默认提供的)

如下所示:我没有写显式写出拷贝构造函数和拷贝赋值函数,编译器会给我们默认提供拷贝构造和拷贝赋值函数

当我们给出上述代码显示的浅拷贝构造时:

INT(const INT &my):value(my.value),ladder(my.ladder)//用初始化列表初始化的
{
    cout << "拷贝构造函数 " << this << endl;
}

看到这里我们会明白当使用对象初始化对象时,系统会默认的给出拷贝构造函数(俗称浅拷贝)进行初始化,让我们看下述代码,思考一下为什么会报错呢?

class MyString
{
private:
	char* str;
public:
	MyString(const char* p = nullptr) :str(nullptr)
	{
		if (p != nullptr)
		{
			int n = strlen(p) + 1;
			str = new char[n];
			strcpy_s(str, n, p);
		}
		else
		{
			str = new char[1];
			*str = '\0';
		}
		cout << "构造函数调用" << this << endl;
	}
	MyString(const MyString& m) :str(m.str)//浅拷贝
	{
		cout << "浅拷贝调用" << this << endl;
	}

	~MyString()
	{
		delete[]str;
		str = nullptr;
		cout << "析构调用" << this << endl;
	}
	void Print()
	{
		cout << "内容是: " << str << endl;
	}

};
int main()
{
	MyString h1("i love you");
	MyString h2(h1);
	
	h1.Print();
	h2.Print();
	return 0;
}

原因是:当数据成员中有指针时,如果采用简单的浅拷贝,则两类中的两个指针将指向同一个地址,当对象快结束时,会调用两次析构函数,同一块空间会被释放两次,而导致指针悬挂现象

上述代码在内存的分布如下:

在我们编写有的代码时,不可能数据成员不会存在指针,或者过程中不进行动态分配内存的操作,那么如何解决问题这就引出了下面什么是深拷贝的问题。

什么是深拷贝?

当类持有其它资源时,例如动态分配的内存、指向其他数据的指针等,默认的拷贝构造函数就不能拷贝这些资源了,我们必须显式地定义拷贝构造函数,以完整地拷贝对象的所有数据。这就是深拷贝。

  • 深拷贝与浅拷贝的区别就在于深拷贝会在堆内存中另外申请空间来储存数据,从而也就解决了指针悬挂的问题。简而言之,当数据成员中有指针时,必须要用深拷贝。

对于上述问题代码,具体做法便是重新开辟一个内存空间存放数据,利用深拷贝构造函数使h2的指针指向新开辟的空间,这样,h1与h2各指向一块堆区的数据,两块空间存放的数据是一样的,这样就在拷贝的同时避免了对同一空间的重复释放问题。 

将上述浅拷贝更改为深拷贝:

	MyString(const MyString& m) //深拷贝
	{
		int n = strlen(m.str) + 1;
		str = new char[n];
		strcpy_s(str, n, m.str);
		cout << "深拷贝调用" << this << endl;
	}

总结:

C++默认生成的拷贝构造函数他的行为是浅拷贝,他只会复制一个一模一样的指针,并不会操作指针指向的东西,想要实现我们的逻辑需求,就需要自定义拷贝构造函数,实现深拷贝。

最后的最后让我再用大白话对于深浅拷贝做一个描述,希望看过之后能对其有着深刻的理解

  1. 当我们有一个房子类时,我们向C++开发商购买了一套房子(声明了一个房子对象),初始化时拿到了房子的钥匙,此时有一个人也向C++购买了一套房子,C++开发商通过对象初始化对象的方法,让另一个拿到了他房子的钥匙,当他去住房时突然发现,诶?怎么房子有人住了(也就是我),这就相当于我和他共住了同一套房子(地址相同),我和他的钥匙都指向这一套房子,当我们办理拆迁时(调用析构函数时),一个房子总不可能拆迁两次吧,这就出现了问题。这就是浅拷贝,只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存。
  2. 当我购买了一个130平的房子时,此时另一个人也来买房,C++开发商通过对象初始化对象的方法,根据我买的130平大小的房子,给另一个人也找了套同样大小的房子,配了钥匙,此时我和他就是各自住一套房子,房子地址不同,当办理拆迁时,互不影响,你拿你的拆迁费,我拿我的。这就是深拷贝,是开辟一块新的内存地址,将原对象的各个属性逐个复制进去,新对象跟原对象不共享内存,对拷贝对象和源对象各自的操作互不影响。

点赞收藏是对博主最大的鼓励!感谢! 

设计并实现一个动态整型数组类Vect,要求: (1)实现构造函数重载,可以根据指定的元素个数动态创建初始值为0的整型数组,或根据指定的内置整型数组动态创建整型数组。 (2)设计拷贝构造函数和析构函数,注意使用深拷贝。 (3)设计存取指定位置的数组元素的公有成员函数,并进行下标越界,若越界则输出“out of boundary”。 (4)设计获取数组元素个数的公有成员函数。 (5)设计用于输出数组元素的公有成员函数,元素之间以空格分隔,最后以换行符结束。 在main函数中按以下顺序操作: (1)根据内置的静态整型数组{1,2,3,4,5}构造数组对象v1,根据输入的整型数构造数组对象v2。 (2)调用Vect的成员函数依次输出v1和v2的所有元素。 (3)输入指定的下标及对应的整型数,设置数组对象v1的指定元素。 (4)根据数组对象v1拷贝构造数组对象v3。 (5)调用Vect的成员函数依次输出v1和v3的所有元素。 设计并实现一个动态整型数组类Vect,要求: (1)实现构造函数重载,可以根据指定的元素个数动态创建初始值为0的整型数组,或根据指定的内置整型数组动态创建整型数组。 (2)设计拷贝构造函数和析构函数,注意使用深拷贝。 (3)设计存取指定位置的数组元素的公有成员函数,并进行下标越界,若越界则输出“out of boundary”。 (4)设计获取数组元素个数的公有成员函数。 (5)设计用于输出数组元素的公有成员函数,元素之间以空格分隔,最后以换行符结束。 在main函数中按以下顺序操作: (1)根据内置的静态整型数组{1,2,3,4,5}构造数组对象v1,根据输入的整型数构造数组对象v2。 (2)调用Vect的成员函数依次输出v1和v2的所有元素。 (3)输入指定的下标及对应的整型数,设置数组对象v1的指定元素。 (4)根据数组对象v1拷贝构造数组对象v3。 (5)调用Vect的成员函数依次输出v1和v3的所有元素。
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

BearPot

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值