浅拷贝:
简单的赋值拷贝操作。
深拷贝:
在堆区重新申请空间,进行拷贝操作。
首先我们先写这样的一段代码:
#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);//new的数据是开到堆区,所以要拿指针接收。cout << "Person的有参构造函数调用" << endl;}~Person() {//堆区开辟的数据由程序员手动开辟,也由程序员手动释放。//析构代码,将堆区开辟的数据做释放。if (m_Height != NULL) {delete m_Height;m_Height = NULL;}cout << "~Person的析构函数调用" << endl;}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;
}
程序居然运行不了。
为什么呢?
让我们看下面这张图:
执行析构代码的时候,由于栈的规则,先进后出,所以p2先被释放,第一次释放p2所指的160的地址被释放,然后开始释放p1,但是由于刚刚释放p2时,把p1和p2共同指向160的那个地址释放了,所以这里p1再次释放那块内存就是非法操作了。注意:这里的if(m_Height !=NULL)是指指针不为空,不是指指针所指的地址不为空。
Person p2(p1)
如果利用编译器提供的拷贝构造函数,会做浅拷贝。
浅拷贝会带来的问题是:
堆区内存重复释放
现在我们要怎么解决这个问题呢?
浅拷贝的问题要利用深拷贝进行解决。
代码如下:
#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);//new的数据是开到堆区,所以要拿指针接收。cout << "Person的有参构造函数调用" << endl;}//自己实现拷贝构造函数 解决浅拷贝带来的问题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() {//堆区开辟的数据由程序员手动开辟,也由程序员手动释放。//析构代码,将堆区开辟的数据做释放。if (m_Height != NULL) {delete m_Height;m_Height = NULL;}cout << "~Person的析构函数调用" << endl;}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;
}
接下来请看这张图:
所以此时释放p2,p1的时候,所释放m_Height的地址是不一样的地址,所以就没有问题了。
总结:
如果属性有在堆区开辟的,一定要自己提供拷贝构造函数,防止浅拷贝带来的问题。