您可以自由地共享和演绎本文稿, 只需遵守开源协议[CC By 4.0]
如果这篇文章帮助到你, 可以请我喝一杯咖啡~
这是Effective C++的学习记录。
条款10:赋值运算符应返回引用
C++定义赋值语句是右结合的,允许连续赋值运算:
a = b = c = d = e = 1;
这就要求了赋值语句的返回值。
但是,为什么只建议返回引用而不是值呢?这是因为传值会引入临时对象实例副本,降低了运行效率。
但是,这一条款只是建议性的,你也可以编写void类型的赋值运算符,而静态检查只会给你报一个warning。
这一规则也适用于运算赋值运算符如+= -=
。
条款11:适配自我赋值
在某些情况下,类的用户可能会使一个实例给自己赋值,此时需要保证不会出现任何异常情况。
初次接触这一条款的时候你可能会不太明白:为什么要特别关注自我赋值的情况呢?
是因为有些对象的赋值运算中会出现指针操作,而指针的再赋值很可能会引入内存空间的释放:
example& example::operator=(const example& val) {
delete this->arr;
this->arr = new array(*val.arr);
return *this;
}
这个例程显然是自我赋值不安全的。解决这个问题最直观的策略是引入额外的条件判断或调整实际的执行顺序。
example& example::operator=(const example& val) {
if (this == &val) return *this;
delete this->arr;
this->arr = new array(*val.arr);
return *this;
}
example& example::operator=(const example& val) {
array* tempPtA = this->arr;
this->arr = new array(*val.arr);
delete tempPtA;
return *this;
}
第二个实现的效果尤为显著,它不仅适配了自我赋值情形,也对new语句可能发生的异常进行了适配:如果new抛出了异常,那么delete不会执行。
当然,二者结合使用是在自我赋值时运行效率最高的,但是额外的条件判断会增大源码和目标码的占用空间,并导入一个控制流减慢运行。
对于右值的实参,我们可以使用复制和交换方式实现:
example& example::operator=(const example val) {
swap(*this, val);
return *this;
}