C++之浅拷贝、深拷贝及相关应用

直接看一个例子

// test.cpp
#include <iostream>
using namespace std;

class Test
{
	public:
	
	int m_num;
	int *m_data;

	Test(int num)
	{
		cout << "call constructor function..1 param.." << endl;
		m_num = num;
		m_data = new int[m_num];
	}

	~Test()
	{
		cout << "call destructor function.." << endl;
		delete []m_data;
	}
};

int main()
{
	Test a(1);
	Test b(a);
	
	return 0;
}

// compile cmd
// g++ -g test.cpp -o test

运行该程序,会coredump,这是为什么呢?现在就引出浅拷贝的概念。
在对一个对象赋值给另一个对象的时候,如果只是进行数据成员间值的简单拷贝,即“浅拷贝”默认拷贝构造函数干的事,关于默认拷贝构造函数可以参考点击我),比如上面这个例子,b.m_num=a.m_num; b.m_data=a.m_data;。注意,m_data可是个指针啊,那么此时,a.m_data和b.m_data就指向同一块内存区域。在程序结束时,a对象和b对象都会析构,a对象析构的时候,调用自定义析构函数会释放a.m_data指向的堆内存(代码中的delete操作),而当b对象析构的时候,又会执行一次delete操作,而且还是同一个地址(因为b.m_data=a.m_data),这就coredump掉了。

那应该如何修复上述问题呢?,现在就引出深拷贝的概念。
“深拷贝”指的是当拷贝对象中有对其他资源(如堆内存、文件、系统等)的引用时(这里的引用可以是指针* 或者 引用&),新对象要另开辟一块新的资源,而不再是对被拷贝对象中 对其他资源的引用(指针* 或者 引用&)进行单纯的赋值操作,然后同步拷贝开辟空间的值。
修复代码如下:

// test.cpp
#include <iostream>
#include <string.h>
using namespace std;

class Test
{
	public:
	
	int m_num;
	int *m_data;

	Test(int num)
	{
		cout << "call constructor function..1 param.." << endl;
		m_num = num;
		m_data = new int[m_num];
	}
	
	Test(const Test& obj)
	{
		cout << "call copy constructor function.." << endl;
		m_num = obj.m_num;						//浅拷贝
		m_data = new int[m_num];				
		memcpy(m_data, obj.m_data, m_num);		//自定义深拷贝
	}

	~Test()
	{
		cout << "call destructor function.." << endl;
		delete []m_data;
	}
};

int main()
{
	Test a(1);
	Test b(a);
	
	return 0;
}

// compile cmd
// g++ -g test.cpp -o test

扩展延伸

现在考虑几个问题,

Q1、STL里的一些泛型算法,如copy,是深拷贝还是浅拷贝?

我们先看看copy的实现方式,可以查看http://www.cplusplus.com/reference/algorithm/copy/?kw=copy
在这里插入图片描述
所以STL的copy是深拷贝还是浅拷贝取决于class T的拷贝构造函数是怎么定义的。如果没有自定义拷贝构造函数,那么就是默认拷贝构造函数,就是浅拷贝;如果自定义了拷贝构造函数,那么看自定义拷贝构造函数里是怎么实现的,如果有深拷贝动作,那么STL的copy对class T就是深拷贝,如果只有浅拷贝动作,那么STL的copy对class T就是浅拷贝。(关于自定义拷贝构造函数和默认拷贝构造函数可以参考点击我

Q2、STL里的一些泛型对比算法,如find,是浅对比还是深对比?

我们先看看find的实现方式,可以查看http://www.cplusplus.com/reference/algorithm/find/?kw=find
在这里插入图片描述
所以,find是深对比(两个对象是否完全一样,包含深拷贝的东西)还是浅对比(仅仅值比较),取决于class T对“==”操作符的重载函数,看这个重载函数是怎么判断的。

<think>好的,我现在需要帮用户理解C++中的浅拷贝深拷贝的区别及实现方式。根据用户提供的引用资料,我需要整理出浅拷贝深拷贝的定义、区别、问题以及如何实现。首先,用户提到的引用[1]到[5]都涉及到这两个概念,所以我需要从这些引用中提取关键信息。 首先,浅拷贝是默认的拷贝方式,也就是编译器自动生成的拷贝构造函数或赋值运算符。它进行的是简单的值复制。例如,引用[4]提到,当类中有指针指向堆区数据时,浅拷贝会导致两个对象的指针指向同一块内存。这可能会引发问题,比如双重释放或者悬垂指针。引用[5]中的例子也说明了这一点,两个对象的指针指向同一块空间,析构时会出现问题。 接下来是深拷贝深拷贝需要用户自己定义拷贝构造函数赋值运算符,为每个对象分配独立的内存空间,并复制内容。引用[3]中的代码示例展示了如何实现深拷贝,通过重新分配内存并复制数据,避免了指针指向同一地址的问题。这样在析构时就不会出现重复释放的情况。 然后,我需要区分两者的区别。浅拷贝的问题在于资源共享可能导致错误,而深拷贝通过独立资源解决了这个问题。引用[2]明确指出了这一点,深拷贝需要新建堆区空间并进行拷贝,而浅拷贝只是简单的值复制。 在实现方式上,用户需要自定义拷贝构造函数赋值运算符,使用new操作符分配内存,并复制数据。引用[3][4]的代码示例展示了如何正确实现深拷贝,确保每个对象拥有自己的资源。 用户可能存在的疑问是,什么时候需要深拷贝?当类中有动态分配的资源(如指针)时,必须使用深拷贝,否则会导致问题。例如,引用[4]中的Person类,如果使用浅拷贝,两个对象的m_Age指针指向同一个地址,当其中一个对象析构后,另一个对象的指针就悬空了。 此外,用户可能想知道如何避免浅拷贝的问题。除了实现深拷贝,还可以使用智能指针来管理资源,如std::unique_ptr或std::shared_ptr,这些智能指针会自动管理内存,减少手动深拷贝的需要。不过这可能超出当前问题的范围,但可以作为扩展知识提及。 总结一下,回答需要涵盖以下几点: 1. 浅拷贝深拷贝的定义。 2. 两者的区别,特别是资源管理方面。 3. 浅拷贝可能导致的问题,如双重释放。 4. 如何通过自定义拷贝构造函数赋值运算符实现深拷贝。 5. 示例代码说明两者的不同实现。 需要确保回答结构清晰,分点说明,并引用提供的资料作为支持。例如,引用[3][4]中的代码示例可以帮助用户更好地理解实现方式。同时,注意使用中文,并按照用户的要求,将数学表达式代码块正确格式化。最后生成相关问题,帮助用户进一步学习。</think>### C++浅拷贝深拷贝的区别及实现方式 #### 1. **基本概念** - **浅拷贝(Shallow Copy)** 默认由编译器生成的拷贝构造函数或赋值运算符实现,仅对成员变量进行**简单值复制**。 - **问题**:若成员变量包含指向堆内存的指针,拷贝后多个对象的指针会指向同一块内存。析构时可能导致**重复释放内存**(双重释放错误)或**悬垂指针**[^4][^5]。 - 示例代码(浅拷贝问题): ```cpp class Person { public: Person(int age) { m_Age = new int(age); } int* m_Age; // 指针指向堆内存 }; Person p1(10); Person p2(p1); // 浅拷贝后,p1.m_Age p2.m_Age 指向同一地址 ``` - **深拷贝(Deep Copy)** 需用户**显式定义拷贝构造函数赋值运算符**,为每个对象分配独立的内存并复制数据。 - **优点**:避免指针共享,确保资源独立性[^2][^3]。 - 示例代码(深拷贝实现): ```cpp class A { public: A(int i=0) { m_pi = new int(i); } A(const A& that) { // 自定义深拷贝构造函数 m_pi = new int; // 新分配内存 *m_pi = *that.m_pi; // 复制数据 } ~A() { delete m_pi; } int* m_pi; }; ``` --- #### 2. **核心区别** | 特性 | 浅拷贝 | 深拷贝 | |---------------|----------------------------------------|----------------------------------------| | **资源管理** | 共享资源(同一块内存) | 独立资源(各自分配内存) | | **实现方式** | 编译器自动生成 | 需手动实现拷贝构造函数赋值运算符[^1] | | **安全性** | 易引发重复释放或数据篡改[^4] | 避免内存错误,安全性高 | --- #### 3. **实现深拷贝的步骤** 1. **定义拷贝构造函数** 分配新内存并复制数据: ```cpp A(const A& that) { m_pi = new int(*that.m_pi); // 直接一步完成分配+复制 } ``` 2. **重载赋值运算符** 避免自赋值并释放旧内存: ```cpp A& operator=(const A& that) { if (this != &that) { delete m_pi; // 释放原有内存 m_pi = new int(*that.m_pi); } return *this; } ``` --- #### 4. **应用场景** - **必须使用深拷贝**:类中包含**动态分配的资源**(如指针、文件句柄)[^4]。 - **浅拷贝适用场景**:仅含基本类型或静态资源(无需管理生命周期)。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值