小编典典

C++11 的三规则变成五规则?

all

所以,在看了这个关于右值引用的精彩讲座之后,我认为每个类都会受益于这样的“移动构造函数”、template<class T> MyClass(T&& other)
编辑 ,当然还有“移动赋值运算符”,template<class T> MyClass& operator=(T&& other)正如
Philipp 在他的回答中指出的那样,如果它是动态分配的成员,或通常存储指针。就像如果前面提到的要点适用,你
应该有一个复制ctor、赋值运算符和析构函数。 想法?


阅读 90

收藏
2022-04-04

共1个答案

小编典典

我会说三法则变成三、四、五法则:

每个类都应明确定义以下一组特殊成员函数之一:

  • 没有
  • 析构函数、复制构造函数、复制赋值运算符

此外,显式定义析构函数的每个类都可以显式定义移动构造函数和/或移动赋值运算符。

通常,以下一组特殊成员函数是合理的:

  • 无(对于隐式生成的特殊成员函数正确且快速的许多简单类)
  • 析构函数、复制构造函数、复制赋值运算符(在这种情况下,类将不可移动)
  • 析构函数、移动构造函数、移动赋值运算符(在这种情况下,类将不可复制,对于底层资源不可复制的资源管理类很有用)
  • 析构函数、复制构造函数、复制赋值运算符、移动构造函数(由于复制省略,如果复制赋值运算符按值获取其参数,则没有开销)
  • 析构函数、复制构造函数、复制赋值运算符、移动构造函数、移动赋值运算符

笔记:

  • 对于显式声明任何其他特殊成员函数(如析构函数或复制构造函数或移动赋值运算符)的类,不会生成移动构造函数和移动赋值运算符。
  • 不会为显式声明移动构造函数或移动赋值运算符的类生成复制构造函数和复制赋值运算符。
  • 并且具有显式声明的析构函数和隐式定义的复制构造函数或隐式定义的复制赋值运算符的类被视为已弃用。

特别是,以下完全有效的 C++03 多态基类:

class C {
  virtual ~C() { }   // allow subtype polymorphism
};

应改写如下:

class C {
  C(const C&) = default;               // Copy constructor
  C(C&&) = default;                    // Move constructor
  C& operator=(const C&) = default;  // Copy assignment operator
  C& operator=(C&&) = default;       // Move assignment operator
  virtual ~C() { }                     // Destructor
};

有点烦人,但可能比替代方案更好(在这种情况下,自动生成 用于复制的特殊成员函数,没有移动的可能性)。

与不遵守规则会导致严重损害的三巨头规则相反,不明确声明移动构造函数和移动赋值运算符通常很好,但在效率方面往往不是最优的。如上所述,移动构造函数和移动赋值运算符只有在没有显式声明的复制构造函数、复制赋值运算符或析构函数时才会生成。这在复制构造函数和复制赋值运算符的自动生成方面与传统的
C03 行为不对称,但更安全。因此,定义移动构造函数和移动赋值运算符的可能性非常有用,并创造了新的可能性(纯可移动的类),但遵守 C03
三巨头规则的类仍然可以。

对于资源管理类,如果无法复制基础资源,您可以将复制构造函数和复制赋值运算符定义为已删除(视为定义)。通常您仍然需要移动构造函数和移动赋值运算符。复制和移动赋值运算符通常使用
来实现swap,如在 C++03
中。谈论swap;如果我们已经有一个移动构造函数和移动赋值运算符,那么特std::swap化将变得
不重要 ,因为泛型std::swap使用移动构造函数和移动赋值运算符(如果可用的话)(这应该足够快)。

不用于资源管理(即没有非空析构函数)或子类型多态性(即没有虚拟析构函数)的类不应声明五个特殊成员函数中的任何一个;它们都将自动生成,并且行为正确且快速。

2022-04-04