1.对象的拷贝造成的指针悬挂
拷贝构造函数是类的一个特殊的成员函数,它的作用是用一个已存在的对象去拷贝出来另一个数据成员值相同的对象。它的参数是本类对象的一个引用,如果不自己编写拷贝构造,编译器会提供一个拷贝构造,其函数体是对类数据成员的赋值语句
#define _CRT_SECURE_NO_WARNINGS
#include<string>
#include<stdlib.h>
#include<assert.h>
#include<iostream>
using namespace std;
class person
{
public:
person() = default;
person(const char* name_, const int age_);//有参构造
~person();//析构函数
void show()
{
cout << name <<" " << age << endl;
}
private:
char* name;
int age;
};
person::person(const char* name_, const int age_)//有参构造类外定义
{
cout << "对象地址为" << this << "的有参构造执行" << endl;
name = new char[strlen(name_) + 1];
assert(name != nullptr);
strcpy(name, name_);
age = age_;
}
person::~person()//析构函数类外定义
{
cout << "对象地址为" << this << "的析构执行" << endl;
if (name != nullptr)
delete[] name;
name = nullptr;
}
int main()
{
person object1("张三", 11);
object1.show();
person object2(object1);
object2.show();
return 0;
}
执行结果:
可以看到object1调用了有参构造,并且正确打印,使用object1拷贝object2也成功并打印,但类中并没书写拷贝构造,说明编译器确实提供了拷贝构造。接着函数即将返回,释放局部对象,执行析构函数,先对object2对象的name指针指向空间做释放,然后释放object2的空间,接着对object1做同object2的操作,但是释放object2的name指针指向空间时发生了断点,这是因为在拷贝时,是直接将object1的name成员的值直接赋值给object2的name成员,这两个对象的name指针指向同一块内存空间,导致在析构的时候二次释放,从而引发断点
如何解决这种问题,其实也很简单:
person::person(const person& p)
{
name = new char[strlen(p.name) + 1];
strcpy(name, p.name);
age = p.age;
}
自己写一个构造函数,为对象name成员开辟一块堆内存空间,只是将待拷贝对象的name成员所指向的空间的值进行拷贝,这样objec1和object2的name成员就指向不同的内存,只是值相同罢了
2.对象的赋值造成的指针悬挂
内置的数据类型的对象可以用等号进行赋值运算,那么类类型的对象可以用等号做赋值运算吗?答案是可行的,这个操作通过对赋值运算符重载来实现,重载运算符实际上就是编写一个函数,函数名由operator关键字后接要重载的运算符,参数是运算符要运算对象的引用。上述person类编译器提供的运算符重载函数大至如下:
person& person:: operator=(const person& source)
{
name = source.name;//指针成员name的拷贝
age = cource.age;// int成员age的拷贝
return *this;
}
这样引起的指针悬挂问题跟拷贝造成的指针悬挂问题很类似,拷贝不过是通过一个对象创建另一个对象,赋值是通过已存在的两个对象的赋值操作,他们最终导致的问题都是两个对象各自的指针成员都指向了同一块内存空间,导致重复释放,将上述赋值运算符重载改写如下:
person& person:: operator=(const person& source)
{
if(this!= &source)//检查是否是同一对象
{
delete[] name;//先释放name的空间
name = new char[strlen(source.name)+1];//开辟新空间
assert(name!=nullptr);
strcpy(name,source.name);//指针成员name的拷贝
age = source.age;// int成员age的拷贝
}
return *this;
}
总之,就是不能让对象的指针类型的数据成员指向同一块内存