深拷贝与浅拷贝
浅拷贝:简单的赋值拷贝操作
深拷贝:在堆区重新申请空间,进行拷贝操作
#include<iostream>
using namespace std;
//深拷贝与浅拷贝
class Person {
public:
Person() {
cout << "Person的默认构造函数调用" << endl;
}
Person(int age , int height) {
cout << "Person的有参构造函数调用" << endl;
m_Age = age;
m_Height = new int(height);//堆区的数据返回的是指针
}
//自己实现拷贝构造函数,解决浅拷贝带来的问题
Person(const Person& p) {
cout << "Person的拷贝构造函数调用" << endl;
m_Age = p.m_Age;
//m_Height = p.m_Height;//编译器默认实现的代码
//深拷贝
m_Height = new int(*p.m_Height);
}
~Person() {
//析构函数,将堆区开辟的数据做释放操作
//栈区规则 先进后出 所以 p2先被释放掉
if (m_Height != NULL) {
delete m_Height;//释放
m_Height = NULL;//防止野指针出现
}
cout << "Person的析构函数调用" << endl;
}
public:
int m_Age;//年龄
int *m_Height;//身高,创建在堆区,所以采用指针
};
void test01() {
Person p1(18,160);
cout << "p1的年龄为:" << p1.m_Age << "身高为:" << *p1.m_Height << endl;//指针需要解引用
Person p2(p1);
//编译器提供了默认拷贝构造函数,做了浅拷贝的操作
cout << "p2的年龄为:" << p2.m_Age << "身高为:" << *p2.m_Height << endl;
}
int main() {
test01();
system("pause");
return 0;
}
运行结果
浅拷贝
class Person {
public:
Person() {
cout << "Person的默认构造函数调用" << endl;
}
Person(int age) {
cout << "Person的有参构造函数调用" << endl;
m_Age = age;
}
~Person() {
cout << "Person的析构函数调用" << endl;
}
public:
int m_Age;//年龄
};
void test01() {
Person p1(18);
cout << "p1的年龄为:" << p1.m_Age << endl;
Person p2(p1);
//编译器提供了默认拷贝构造函数,做了浅拷贝的操作
cout << "p2的年龄为:" << p2.m_Age << endl;
}
运行结果
我们未提供拷贝构造函数,但编译器为我们提供了默认拷贝构造函数,因此可以打印出p2的年龄,这种叫做浅拷贝,即编译器为我们提供的拷贝构造函数
接下来我们在堆区创建身高,所以我们要创建指针
class Person {
public:
Person() {
cout << "Person的默认构造函数调用" << endl;
}
Person(int age , int height) {
cout << "Person的有参构造函数调用" << endl;
m_Age = age;
m_Height = new int(height);//堆区的数据返回的是指针
}
~Person() {
cout << "Person的析构函数调用" << endl;
}
public:
int m_Age;//年龄
int *m_Height;//身高,创建在堆区,所以采用指针
};
void test01() {
Person p1(18,160);
cout << "p1的年龄为:" << p1.m_Age << "身高为:" << *p1.m_Height << endl;//指针需要解引用
Person p2(p1);
//编译器提供了默认拷贝构造函数,做了浅拷贝的操作
cout << "p2的年龄为:" << p2.m_Age << "身高为:" << *p2.m_Height << endl;
}
运行结果(在堆区开辟了身高之后的运行结果,依然是浅拷贝)
我们知道在堆区的数据是由程序员手动开辟,手动释放。
什么时候堆区的数据可以释放?
即在test01执行结束之后,我们就可以释放掉堆区的内存,所以此时析构函数有了作用,即将堆区开辟的数据做释放操作
~Person() {
//析构函数,将堆区开辟的数据做释放操作
if (m_Height != NULL) {
delete m_Height;//释放
m_Height = NULL;//防止野指针出现
}
cout << "Person的析构函数调用" << endl;
}
此时运行结果
系统崩溃
为什么会产生错误?
p2中的所有数据全部是拷贝p1中的,当我们释放堆区的数据时(栈区规则,先进后出,因此先释放p2)
即
~Person() {
//析构函数,将堆区开辟的数据做释放操作
//栈区规则 先进后出 所以 p2先被释放掉
if (m_Height != NULL) {
delete m_Height;//释放
m_Height = NULL;//防止野指针出现
}
cout << "Person的析构函数调用" << endl;
}
p2执行析构函数之后,堆区的数据已经为空
此时p1再执行析构函数,即释放堆区数据,但此时堆区的数据已经为空,为非法操作
所有系统崩溃,那我们如何解决这个问题?
由浅拷贝带来的问题要利用深拷贝解决
即自己写拷贝构造函数的代码,在堆区再开辟一段内存,使p2指向它,因此解决内存重复释放的问题
代码的实现
//自己实现拷贝构造函数,解决浅拷贝带来的问题
Person(const Person& p) {
cout << "Person的拷贝构造函数调用" << endl;
m_Age = p.m_Age;
//m_Height = p.m_Height;//编译器默认实现的代码
//深拷贝
m_Height = new int(*p.m_Height);
}
在堆区开辟一段新的内存即 new int ( ),再将160解引用,放入堆区的内存,再将160赋值给新的拷贝变量m_Height。
此时的运行结果
总结:如果属性有在堆区开辟的,一定要自己提供拷贝构造函数,防止浅拷贝带来的问题