移动构造和移动赋值是c++实现零拷贝的利器。对于一个简单的用户类,在满足一定条件时,编译器会提供默认的移动构造和移动赋值实现:默认实现会对类成员变量递归地进行移动构造和移动赋值。如果用户类内包含STL容器这种本身有移动实现的成员变量,又不包含非RAII的资源管理,那么默认移动逻辑是完全够用的。
但是,启用默认移动构造函数必须满足以下全部条件:
- 没有声明拷贝赋值函数。
- 没有声明拷贝构造函数。
- 没有声明移动赋值函数。
- 移动构造函数没有隐式声明为delete(参考这里,简单类一般不需要考虑)。
- 没有声明析构函数。
同时,启用默认移动赋值函数必须满足以下全部条件:
- 没有声明拷贝赋值函数。
- 没有声明拷贝构造函数。
- 没有声明移动构造函数。
- 移动赋值函数没有隐式声明为delete(参考这里,简单类一般不需要考虑)。
- 没有声明析构函数。
第5点是尤其需要注意的。很多同学由于强迫症美观,喜欢声明一个空的析构函数,而这样做会不知不觉地禁用编译器默认的移动构造和移动赋值。如果没有自己显式补充移动构造和移动赋值相应的声明,它们将相应以浅拷贝的形式完成。至于为什么设置第5点其实也很容易理解。因为析构函数不可省略的场景往往涉及资源的手动分配和释放,这时候移动构造和移动赋值一般需要进行诸如交接所有权的额外逻辑。所以在用户类已声明析构函数时,编译器不提供默认的移动逻辑以降低非预期行为的可能性。