CppCoreGuidelines C.60 拷贝赋值/复制赋值不可为虚,且以 const& 为参数,以 非 const& 为结果
25 October 2022
C.60: Make copy assignment non-virtual
, take the parameter by const&
,
and return by non-const&
理由
如此操作,简单高效。如想针对右值进行优化,可提供一个重载的函数,接受一个&&
右值引用作为参数。
例子
class Foo { public: Foo& operator=(const Foo& x) { // GOOD: no need to check for self-assignment (other than performance) auto tmp = x; swap(tmp); // see C.83 return *this; } // ... }; Foo a; Foo b; Foo f(); a = b; // assign lvalue: copy a = f(); // assign rvalue: potentially move
注意
swap
实现技术提供有效保障。
例子
但是,如果你想要避免临时副本,获取更好的性能。考虑一个简单的 Vector
情况,包含元素比较多,且大小一样的 Vector
之间赋值的情况就比较常见。这种情况,用 swap
实现就会导致比较大的性能开销。
template<typename T> class Vector { public: Vector& operator=(const Vector&); // ... private: T* elem; int sz; }; Vector& Vector::operator=(const Vector& a) { if (a.sz > sz) { // ... use the swap technique, it can't be bettered ... return *this; } // ... copy sz elements from *a.elem to elem ... if (a.sz < sz) { // ... destroy the surplus elements in *this and adjust size ... } return *this; }
通过直接改写目标元素,我们不能像 swap 技术那样实现完备的保证,只能达到基础保证。所以需要注意自己给自己赋值这种情况。
其他选项
如果你需要一个虚的赋值操作符,但是明白可能有深层次的问题,那可以不叫它为operator=
,而取名为 virtual void assign(const Foo&)
强化
- (简单)赋值操作符必须不能是
virtual
的。小心恶龙出没。 - (简单)赋值操作符要返回
T&
,从而可以串接起来使用。不要用const T&
,这样会影响代码编排,无法把对象放入容器。 - (中等难度)赋值操作符应该(隐式或显示)地调用基类和成员的赋值操作符。查看析构函数确定类型是否有指针语义或值语义。