【C++基础】浅拷贝与深拷贝

需要的预备知识:
构造函数(无参或默认构造函数、有参构造函数、拷贝构造函数),析构函数,new和delete操作符(堆区内存的开辟与释放)。

浅拷贝

浅拷贝是简单的赋值操作,只拷贝一个指针,没有开辟新的地址,拷贝的指针和原来的指针指向同一块地址,这样调用析构函数释放堆区资源的时候,因为如果原来的指针指向的内存被释放了,后面再释放浅拷贝的指针,会带来内存重复释放的问题,从而报错。
使用编译器默认提供的拷贝构造函数,就是浅拷贝

如下代码来自黑马程序员C++课程,我们new一个指针m_Height来测试下:

#include<iostream>
using namespace std;
//浅拷贝
class Person
{
public:
	Person()
	{
		cout << "Person的默认构造函数调用" << endl;
	}
	Person(int age, int height)
	{
		m_Age = age;
		m_Height = new int(height);//开辟堆区内存,放置height
		cout << "Person的有参构造函数调用" << endl;
	}
	~Person()//析构函数
	{
		//析构代码,将堆区开辟的数据做释放操作
		if (m_Height != nullptr)
		{
			delete m_Height;
			m_Height = nullptr;//防止野指针出现
		}
		cout << "Person的析构函数调用" << endl;
	}
	int m_Age;//年龄
	int *m_Height;//身高,想把身高创建在堆区,new一个,所以定义一个指针
};

void test01()
{
	Person p1(18,160);//有参构造函数调用
	//析构函数里不写delete m_Height,即不释放申请的堆内存,就能正常输出,且对于身高指针p1.m_Height而言,会发现p1.m_Height和p2.m_Height两者一样,同样的地址
	cout << "p1的年龄为" << p1.m_Age << "p1的身高为"<<*p1.m_Height<<endl;
	Person p2(p1);//没自己写拷贝构造函数,用编译器自己提供的,进行了简单的赋值操作(浅拷贝)
	cout << "p2的年龄为" << p2.m_Age << "p2的身高为" <<*p2.m_Height << endl;
}

int main() {
	test01();
	system("pause");
	return 0;
}

不释放m_Height指向的内存,并且输出指针而不解引用时,即代码如下
在这里插入图片描述
在这里插入图片描述
结果如下
可以看到两个地址是
可以看到两个地址是一模一样的,这就是浅拷贝。但是如果执行完整的代码,即在构造函数里执行delete m_Height。会出现以下结果:
在这里插入图片描述
这就是重复释放了内存。因为test01()里有两个局部对象p1和p2,在函数调用完会自动执行构造函数,构造函数里我们写了delete m_Height,即释放指针指向的内存,他会根据先进后出规则,先释放p2的m_Height指向的内存,再释放m_Height指向的内存,而这两个内存是同一块,所以出现重复释放问题。

深拷贝

浅拷贝的问题我们用深拷贝解决!我们不使用编译器提供的拷贝构造函数,而是自己写一个

Person(const Person &p)
	{
		cout << "Person拷贝构造函数调用" << endl;
		m_Age = p.m_Age;
		//编译器默认的拷贝构造其实是m_height = p.m_Height,即直接把p1的m_Height等号赋值给p2的m_Height
		//深拷贝操作
		m_Height = new int(*p.m_Height);//自己实现的话,重新new一个,开辟新的空间
	}

和编译器提供的拷贝构造函数的操作,区别就在于m_Height = new int(*p.m_Height),我们自己重新申请了一个内存空间
完整代码如下

#include<iostream>
using namespace std;
//深拷贝
class Person
{
public:
	Person()
	{
		cout << "Person的默认构造函数调用" << endl;
	}

	Person(int age, int height)
	{
		m_Age = age;
		m_Height = new int(height);//开辟堆区内存,放置height
		cout << "Person的有参构造函数调用" << endl;
	}

	//自己实现拷贝构造函数,解决浅拷贝带来的问题
	Person(const Person &p)
	{
		cout << "Person拷贝构造函数调用" << endl;
		m_Age = p.m_Age;
		//编译器默认的拷贝构造其实是m_height = p.m_Height,即直接把p1的m_Height等号赋值给p2的m_Height
		//深拷贝操作
		m_Height = new int(*p.m_Height);//自己实现的话,重新new一个,开辟新的空间
	}
	~Person()//析构函数
	{
		//析构代码,将堆区开辟的数据做释放操作
		if (m_Height != nullptr)
		{
			delete m_Height;
			m_Height = nullptr;//防止野指针出现
		}
		cout << "Person的析构函数调用" << endl;
	}
	int m_Age;//年龄
	int *m_Height;//身高,想把身高创建在堆区,new一个,所以定义一个指针
};

void test01()
{
	Person p1(18,160);//有参构造函数调用
	//输出身高指针p1.m_Height,会发现p1.m_Height和p2.m_Height两者一样,同样的地址
	cout << "p1的年龄为" << p1.m_Age << "p1的身高为"<<*p1.m_Height<<endl;
	Person p2(p1);//没自己写拷贝构造函数,用编译器自己提供的,进行了简单的赋值操作(浅拷贝)
	cout << "p2的年龄为" << p2.m_Age << "p2的身高为" <<*p2.m_Height << endl;
}

int main() {
	test01();
	system("pause");
	return 0;
}

运行结果
在这里插入图片描述
我们去掉解引用*,输出地址看看
在这里插入图片描述
所以,即使p1和p2两个对象的身高指针都叫m_Height,但是在我们自己实现的拷贝构造函数里,重新给height分配了内存空间,所以两者的地址是不一样的,这样就避免了浅拷贝在调用析构函数时带来的内存重复释放问题。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值