[C++]内存变化图解---深拷贝与浅拷贝的区别

每次学完深拷贝与浅拷贝,过一段时间总会忘记,所以打算根据内存变化做好图解,方便记忆
要了解程序运行时的内存变化,首先得了解栈和堆。
1、栈,就是那些由编译器在需要的时候分配,在不需要的时候自动清除的变量的存储区。里面的变量通常是局部变量,函数参数、对象的引用等。
2、堆,又叫自由存储区,它是在程序执行的过程中动态分配的,它最大的特性就是动态性。由new分配的内存块,他们的释放编译器不去管,由我们的应用程序去控制,一般一个new就要对应一个delete。如果程序员没有释放掉,那么在程序结束后,操作系统会自动回收。如果分配了堆对象,却忘记了释放。就会产生内存泄漏。而如果已释放了对象,却没有将相应的指针置为NULL。该指针就是"悬挂指针"。

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

class Person{
public:
	Person(){

	}
	//初始化属性
	Person(char *name, int age){
		//char* 必须要先开辟空间
		//所以不能直接使用m_Name=name;只能以下列方式
		m_Name = (char*)malloc(strlen(name) + 1);
		strcpy(m_Name, name);

		m_Age = age;
	}
	//拷贝构造,系统会提供默认拷贝函数,而且是简单的值拷贝

	//析构函数
	~Person(){
		cout << "析构函数调用" << endl;
		if (m_Name != NULL){
			free(m_Name);//释放堆上的对象
			m_Name = NULL;//将指针置为空
		}
	}
	//姓名
	char *m_Name;
	//年龄
	int m_Age;
};
void test01(){
	Person p1=Person("张三", 15);//调用有参构造
	Person p2(p1);//调用拷贝构造
}

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

运行结果如图:
在这里插入图片描述

这是由于拷贝构造函数使用浅拷贝所导致的问题。
下面探索程序运行时的内存调用:
首先从main()函数开始运行,进入test01()函数中,执行
Person p1= Person(“张三”, 15);//调用有参构造,下面图片表示,调用有参构造函数之前,注意:假设0x5663为开辟空间的首地址
在这里插入图片描述

调用有参构造函数:到执行完
m_Name = (char*)malloc(strlen(name) + 1);假设空间首地址为0x01234
在这里插入图片描述

执行有参构造函数,到执行完
m_Age = age;
在这里插入图片描述

执行完有参构造函数,销毁局部变量
在这里插入图片描述

执行拷贝构造函数
Person p2(p1);//调用拷贝构造
在这里插入图片描述

接下来,调用析构函数,对p1对象进行操作。

   ~Person(){
	cout << "析构函数调用" << endl;
	if (m_Name != NULL){
		free(m_Name);//释放堆上的对象
		m_Name = NULL;//将指针置为空
	}
}

将堆上的对象释放,并将指针置为空。
在这里插入图片描述

之后,对p2对象进行析构,由于p2所指向的m_Name属性也是0x01234这块内存空间,它在p1析构时,已经被释放了,并将指针置为空了,此时又需要被释放,并将指针置为空,所以运行时,程序会报错。
简单来说,浅拷贝就是将一个对象首地址的值,赋值给另一个对象,即只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存。无论哪个对象发生改变,其实都是改变存储空间的内容。

解决上述问题的办法:使用深拷贝,即另外创造一个一模一样的对象,新对象和原对象不共享内存,修改新对象不会导致原对象的内存发生改变

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

class Person{
public:
	Person(){

	}
	//初始化属性
	Person(char *name, int age){
		//char* 必须要先开辟空间
		//所以不能直接使用m_Name=name;只能以下列方式
		m_Name = (char*)malloc(strlen(name) + 1);
		strcpy(m_Name, name);

		m_Age = age;
	}
	//拷贝构造,系统会提供默认拷贝函数,而且是简单的值拷贝
	//自己提供拷贝构造,原因是简单的浅拷贝会释放堆区空间2次,导致程序崩溃
	//深拷贝
	Person(const Person&p){
		m_Name = (char*)malloc(strlen(p.m_Name) + 1);
		strcpy(m_Name, p.m_Name);
		m_Age = p.m_Age;
	}
	//析构函数
	~Person(){
		cout << "析构函数调用" << endl;
		if (m_Name != NULL){
			free(m_Name);//释放堆上的对象
			m_Name = NULL;//将指针置为空
		}
	}
	//姓名
	char *m_Name;
	//年龄
	int m_Age;
};
void test01(){
	Person p1=Person("张三", 15);//调用有参构造
	Person p2(p1);//调用拷贝构造
}

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

运行结果如图:
在这里插入图片描述
深拷贝内存变化:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值