谈谈C++ 中深浅拷贝的问题

构造函数的潜在问题风险

对于成员变量是有指针类型的类来说,构造函数初始化成员时候,很容易就会出大问题;

举个例子:

class Car
{
public:
	Car(int price,const char* name):m_price(price),m_name(name)
	{
	}
private:
	int m_price;
	char* m_name; //Car类有指针类型的成员变量
};

int main()
{
	char name[] = {"b","m","w",“\0};
	Car* car  = new car(100,name);
	
	return 0;	
}

观察上面程序,画出内存布局:
在这里插入图片描述

我们发现:堆空间的成员变量m_name指针指向了栈空间name;这会导致一个非常严重的问题,假如栈的name生命周期结束了,而堆的内存空间还没有没被释放,那么堆空间中m_name就会指向一个非法内存,成为了野指针;


所以说:一个堆空间的指针,指向了栈空间的数据,都很容易导致大问题出现,这是要避免的


解决构造函数的潜在风险方案

那么如何解决这个问题呢?其实很简单:只要不然堆空间的指针指向栈空间即可,也就是堆空间的指针也只想堆空间;

那么我们只要修改以下构造函数的书写方式就可以,同时不要忘记释放内存:如下代码:

class Car
{
public:
	Car(int price,const char* name):m_price(price)
	{
		if(name == nullptr) return;
		m_name = new char[strlen(name)+1]; //让成员变量的指针,指向堆空间
		strcpy(m_name,name); //再把栈空间的name数据拷贝给m_name
	}
	~Car()
	{
		if(m_name == nullptr) return;
		delete[] m_name;
		m_name = nullptr;
	}
private:
	int m_price;
	char* m_name; //Car类有指针类型的成员变量
};

int main()
{
	char name[] = {"b","m","w",“\0};
	Car* car  = new car(100,name);
	
	return 0;	
}

在这里插入图片描述


刚刚的例子是:对象在堆空间,并且堆空间的成员变量指向栈空间的数据,会出大问题;
现在又有个问题是:假如我的对象是在栈空间,并且有多个栈空间对象指向了堆空间的一个数据成员,这又会发生什么事情呢?


浅拷贝

编译器提供的默认拷贝构造就是浅拷贝;
并且浅拷贝有一个特点就是:它会按照字节序的一个一个字节将数据成员变量拷贝到另一个对象;

浅拷贝对于非指针类型的成员变量拷贝无太大关系,但是一旦类内部有指针成员变量,并且该指针指向了堆空间的话,那么就很容易出大问题;


class Car
{
public:
	Car(int price,const char* name):m_price(price)
	{
		if(name == nullptr) return;
		m_name = new char[strlen(name)+1]; //让成员变量的指针,指向堆空间
		strcpy(m_name,name); //再把栈空间的name数据拷贝给m_name
	}
	~Car()
	{
		if(m_name == nullptr) return;
		delete[] m_name;
		m_name = nullptr;
	}
private:
	int m_price;
	char* m_name; //Car类有指针类型的成员变量
};

int main()
{
	char name[] = {"b","m","w",“\0};
	Car car1(100,name);
	Car car2 = car1; //注意这里会调用拷贝构造,但是我没有写所以调用默认的拷贝构造	
	return 0;	
}

注意观察,上面的初始化会发生什么事?
画出内存布局图
在这里插入图片描述


很明显:上面的两个对象,一旦出现了两次析构之后,堆空间的同一份内存数据被释放了两次,这就会导致一个大问题的出现,程序会直接崩溃了;
这就是浅拷贝带来的隐患


深拷贝

深拷贝主要做的事情就是:将指针的内容拷贝到新的对象空间中;

解决这个问题也很容易:只要自己实现拷贝构造就可以:
那自己实现的拷贝构造要做的事情就是拷贝多一份堆空间的数据即可,让不一样的对象指向不同的堆空间的数据;


修改以下上面的浅拷贝代码:
如下:

class Car
{
public:
	Car(int price,const char* name):m_price(price)
	{
		if(name == nullptr) return;
		m_name = new char[strlen(name)+1]; //让成员变量的指针,指向堆空间
		strcpy(m_name,name); //再把栈空间的name数据拷贝给m_name
	}
	Car(const Car& car):m_price(car.m_price) //自己写的深拷贝
	{
		if(car.name == nullptr) return;
		m_name = new char[strlen(car.name)+1];
		strcpy(m_name,car.name);
	}
	~Car()
	{
		if(m_name == nullptr) return;
		delete[] m_name;
		m_name = nullptr;
	}
private:
	int m_price;
	char* m_name; //Car类有指针类型的成员变量
};

int main()
{
	char name[] = {"b","m","w",“\0};
	Car car1(100,name);
	Car car2 = car1; //注意这里会调用拷贝构造,但是我没有写所以调用默认的拷贝构造	
	return 0;	
}

在这里插入图片描述


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

呋喃吖

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值