每次学完深拷贝与浅拷贝,过一段时间总会忘记,所以打算根据内存变化做好图解,方便记忆
要了解程序运行时的内存变化,首先得了解栈和堆。
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;
}
运行结果如图:
深拷贝内存变化: