假设有一个如下的MyClass类:
class MyClass { public: //构造函数 //拷贝构造函数 MyClass(const MyClass& that) : int_data_(that.int_data_), dbl_data_(that.dbl_data_), str_data_(that.str_data_) { } //赋值操作符 MyClass& operator = (const MyClass& that) { if(this != that) { int_data_ = that.int_data_; dbl_data_ = that.dbl_data_; str_data_ = that.str_data_; } return *this; } //一些其他方法 private: Int int_data_; Double dbl_data_; string str_data_; //每次在这里添加一个新的数据成员时,不要忘了在拷贝构造函数和赋值操作中添加对应的代码 };
对于这个类,存在什么问题呢?真正的问题是,在私有部分后面的注释指出了问题的所在。如果像注释中所说的,这样的操作特别麻烦而且极易犯错误。事实上,如果我们没有编写拷贝构造函数和赋值操作符,C++会为我们编写一个“默认版本”。
拷贝构造函数的默认版本为所有的数据成员调用拷贝构造函数(或简单的复制内置类型),赋值操作符的默认版本将调用每个数据成员的赋值操作符或者简单的复制内置类型。
因此,对于上面的例子,拷贝构造函数和赋值操作符完全不是必需的。更糟的是,它们是潜在的错误之源,因为它们使代码变得脆弱。如果有人试图改变它们,就可能对代码产生破坏。
因此,对于上述的例子,比较好的思路是彻底避免编写拷贝构造函数和赋值操作符。
一般而言,有几种方式可供选择:
- 依赖编译器自动创建的默认版本
- 把拷贝构造函数和赋值操作符声明为私有,并且不提供实现,禁止任何类型的复制
- 编写自己的版本
对于上述三种方式,应该尽量避免使用第三种。如果发现自己为某个类编写了拷贝构造函数或赋值操作符,思考是否有必要。也许可以避免这种做法并转为第一种方式(使用编译器所创建的默认版本)或使用其他方法(例如智能指针)。如果仍然不确定,可以使用第二种方式,只要不存在任何类型的复制,就不会出错。但是,需要注意类的有些用法(例如在vector<MyClass>中)需要拷贝构造函数和赋值操作符,因此禁止任何类型的复制也需要谨慎,必须理解它限制了类的使用方面的某些选项。
总结:
- 只要有可能,避免编写拷贝构造函数和赋值操作符
- 如果默认版本并不适用,可以考虑把拷贝构造函数和赋值操作符声明为私有,禁止类实例的复制
时间: 2024-10-07 08:25:33