现在有一个类Student,声明如下:
class Student {
private:
char* name;
int age;
public:
Student(const char* name, int age);
Student(const Student& other);
virtual ~Student() { delete[] name; };
Student& operator=(const Student& rhs);
// other functions
};
构造函数实现如下,这里我们需要为name开辟新的空间。
Student::Student(const char *name, int age) {
this->name = new char[strlen(name) + 1];
strcpy(this->name, name);
}
赋值操作重载函数实现如下。
Student& Student::operator=(const Student &rhs) {
if (this == &rhs) return *this;
delete [] name;
this->name = new char[strlen(rhs.name) + 1];
strcpy(this->name, rhs.name);
}
拷贝构造函数还没写,那这里我偷个懒,直接用赋值操作重载函数给*this赋值可以嘛?就像这样:
Student::Student(const Student &other) {
*this = other;
}
我们想一下:拷贝构造函数是什么时候用?是在构造全新的对象时!这个时候新创建的对象存放着内存中的垃圾值,此时我们又调用operator=,它会执行delete操作。可以想见指针name存放的也是无效值,故这一步肯定会触发段错误。
解决办法:在调用operator=前,对name赋值nullptr就可以了。
Student::Student(const Student &other) {
name = nullptr;
*this = other;
}
虽然问题是解决了,但这个方法总让我感觉怪怪的。拷贝构造函数非要调用operator=的话,那总是绕不开operator=中的delete操作。派生类对象中调用拷贝构造函数时,可能部分继承的成员指针在初始化列表中就已经被初始化好了,进入operator=中又要被清理掉然后重新分配空间。这种情况下就带来了大量的冗余操作。
不过好处在于,代码量可以大幅减少,类成员有变动时,需要维护的地方变少了。
所以是否要这样写,需要在具体场景仔细权衡。