浅拷贝和深拷贝,在属性有堆区开辟,一定要自己提供拷贝构造函数。
重点看程序注释!
#include <iostream>
#include<string>
using namespace std;
class Person {
public:
Person() {
cout << "无参构造函数!" << endl;
}
//有参构造函数
Person(int a) {
age = a;
cout << "有参构造函数!" << endl;
}
Person(int a,int h) {
age = a;
height = new int(h);//因为我在属性那建立了一个指针,指针只能接受地址,所以我传过来的h,通过开辟堆区空间
//运算的结果是一个地址,所以用指针来接收,你如果不在程序中释放空间,这个程序一定能跑,但是,当你释放空间
//的时候,要注意释放的时机。
cout << "两参数的有参构造函数!" << endl;
}
//拷贝构造函数(先跳到59行看)
Person(const Person& p) {
age = p.age;
//height = p.height;//可以看做是编译器出来的默认拷贝,但是刚也说了,要实现深拷贝,你现在这个只是浅拷贝
height = new int(*p.height);//要注意是解引用,因为我是重新把你的值找一个地址的
cout << "深拷贝构造函数!" << endl;
}
//析构函数
~Person() {
if (height != NULL)
{
delete height;
height = NULL;//为了防止野指针出现,就将他置空
}
cout << "析构函数!" << endl;
}
private:
int age;
int* height;
};
//调用有参的构造函数
void test01() {
Person p1(18);
Person p2(p1);//走的是拷贝构造函数,但是因为程序员没有设置拷贝构造,所以编译器按默认的拷贝构造函数,走,所以数出来的p2.age也是18
//
}
void test02() {
//现在建立两个参数的有参构造
Person p3(18, 15);
//调用结束,Person局部变量被释放,因此此时的15的在此时释放比较好,所以要回到类的析构函数中去释放
//**********以下为关键部分内容,当有成员属性在堆区开辟的时候,涉及调用拷贝构造函数,在释放的时候就要出问题******************
Person p4(p3);
//问题在哪?
//问题在于根据进栈顺序,先进后出,p4的变量先释放,即p4的析构函数被调用,函数体中有一段释放堆区内存的程序,因此p4按照逻辑就把15的内存空间给释放了
//而当p3去运行那段程序时,height的内存早已被释放了,因为程序崩溃!【重复释放内存】
//解决措施:在p4在进行p3拷贝的时候呢,调用自己构造的拷贝函数的时候直接让你那个height换一个地址,这样在释放的时候就各走各的了,实现深拷贝,但你要不在堆区释放空间,就没那么多事吧
}
int main() {
//test01();
test02();
system("pause");
return 0;
}
//如果在类对象属性的定义时,在堆区开辟了空间,那么一定要自己提供拷贝构造函数,实现深拷贝,即第30行的代码,防止浅拷贝带来的问题
|