构造函数的潜在问题风险
对于成员变量是有指针类型的类来说,构造函数初始化成员时候,很容易就会出大问题;
举个例子:
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;
}