【C++】在拷贝构造函数中调用赋值操作重载函数的一些陷阱和利弊讨论

现在有一个类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=中又要被清理掉然后重新分配空间。这种情况下就带来了大量的冗余操作。

不过好处在于,代码量可以大幅减少,类成员有变动时,需要维护的地方变少了。

所以是否要这样写,需要在具体场景仔细权衡。

发表评论

您的电子邮箱地址不会被公开。