C++之构造函数(2)深拷贝和浅拷贝

本文探讨了C++中的浅拷贝和深拷贝概念,特别是在类对象包含堆区分配内存时的重要性。通过示例代码解释了默认拷贝构造函数导致的问题,即浅拷贝在释放内存时可能引发重复释放,导致程序崩溃。解决办法是自定义深拷贝构造函数,确保每个副本拥有独立的堆内存,从而避免此类问题。强调了在类设计中,当属性涉及堆区开辟时,提供深拷贝构造函数的必要性。

浅拷贝和深拷贝,在属性有堆区开辟,一定要自己提供拷贝构造函数。

重点看程序注释!

#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行的代码,防止浅拷贝带来的问题

### C++拷贝构造函数深拷贝浅拷贝 #### 概述 在 C++ 中,当创建一个新对象并将其初始化为另一个已存在对象时,会调用拷贝构造函数。如果未显式定义拷贝构造函数,编译器会提供一个默认版本,该版本通常执行的是浅拷贝。 #### 浅拷贝 浅拷贝是指在对象复制过程中,仅仅对对象的数据成员进行简单赋值操作。对于基本数据类型的成员变量,这种行为通常是安全的。然而,当类中包含指针或其他动态分配资源时,浅拷贝可能导致多个对象共享同一块内存域,从而引发潜在问题,比如双重删除或悬空指针等问题[^1]。 例如,在以下代码片段中展示了如何通过系统自动生成的拷贝构造函数实现浅拷贝: ```cpp class CopyDemo { private: int* ptr; public: CopyDemo(int value) : ptr(new int(value)) {} ~CopyDemo() { delete ptr; } }; int main() { CopyDemo obj1(10); CopyDemo obj2 = obj1; // 使用默认拷贝构造函数 } ``` 在此例子中,`obj2.ptr` `obj1.ptr` 将指向相同的地址。一旦其中一个对象被销毁,另一对象尝试访问已被释放的内存,这将导致未定义的行为[^3]。 #### 深拷贝 为了防止上述情况发生,可以手动编写拷贝构造函数以完成深拷贝深拷贝意味着不仅复制原始对象的内容到新的对象里去,而且还会重新分配必要的内存空间给这些内容副本,使得两个对象各自拥有独立的数据副本。 继续以上面提到的 `CopyDemo` 类为例,下面是如何实现深拷贝的一个方式: ```cpp class CopyDemo { private: int* ptr; public: CopyDemo(int value) : ptr(new int(value)) {} // 自定义拷贝构造函数实现深拷贝 CopyDemo(const CopyDemo& other) : ptr(new int(*(other.ptr))) {} ~CopyDemo() { delete ptr; } void DisplayValue() const { cout << *ptr << endl; } }; ``` 在这个改进后的版本中,每当创建一个新的 `CopyDemo` 对象并通过现有实例初始化它的时候,都会为其单独开辟一块存储整数的新位置,并把原数值复制过来,而不是单纯让两者共用同一个指针[^5]。 #### 总结 - **浅拷贝**:由编译器提供的默认拷贝构造函数所做之事;只涉及位级复制。 - **深拷贝**:程序员需自己定义合适的逻辑来确保每一个涉及到的对象都有自己的实体而非引用关系。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值