在这种情况下,一个更重要的问题是:
“这是什么意思,当你以某种方式编写函数时,需要你检查自赋值???”
回答我的反问:这意味着设计良好的赋值运算符不需要检查自赋值。将一个对象分配给它自己应该可以正常工作(即具有“什么都不做”的最终效果),而不需要对自分配进行显式检查。
例如,如果我想按照以下方式实现一个简单的数组类
class array {
// code...
int *data;
size_t n;
};
...并提出了赋值运算符的以下实现...
array &array::operator =(const array &rhs)
{
delete[] data;
n = rhs.n;
data = new int[n];
std::copy_n(rhs.data, n, data);
return *this;
}
该实现将被认为是“糟糕的”,因为它在自我分配的情况下显然会失败。
为了“解决”这个问题,开发人员有两个选择;
- 他或她可以添加明确的自我分配检查
array &array::operator =(const array &rhs) {
if (&rhs != this) {
delete[] data;
n = rhs.n;
data = new int[n];
std::copy_n(rhs.data, n, data);
}
return *this;
}
- 或者您可以遵循“无检查”方法:
array &array::operator =(const array &rhs) {
size_t new_n = rhs.n;
int *new_data = new int[new_n];
std::copy_n(rhs.data, new_n, new_data);
delete[] data;
n = new_n;
data = new_data;
return *this;
}
后一种方法在某种意义上更好,因为它可以在自我分配情况下正常工作,而无需进行明确的检查。从“安全角度”来看,这个实现远非完美,这里是为了说明处理自分配的“检查”和“无检查”方法之间的区别。后面的无检查实现可以通过众所周知的复制和交换习语来更优雅地编写。
这并不意味着您应该避免对自分配进行显式检查。从性能的角度来看,这样的检查确实是有意义的:执行一长串操作只是为了最终“什么都不做”是没有意义的。但在设计良好的赋值运算符中,从正确性的角度来看,这样的检查是不必要的。