五规则

Version >= C++ 11

C++ 11 引入了两个新的特殊成员函数:移动构造函数和移动赋值运算符。由于你想要遵循 C++ 03 中的 Rule of Three 的所有相同的原因,你通常想要遵循 C++ 11 中的 Five of Rule:如果一个类需要五个特殊成员函数中的一个,并且如果移动语义如果需要,那么它很可能需要五个。

但是,请注意,未遵循五法则通常不会被视为错误,而是错过优化机会,只要仍然遵循三法则。如果编译器通常不使用移动构造函数或移动赋值运算符,则它将在可能的情况下使用复制语义,从而导致由于不必要的复制操作而导致操作效率降低。如果类不需要移动语义,则不需要声明移动构造函数或赋值运算符。

与三规则相同的例子:

class Person
{
    char* name;
    int age;

public:
    // Destructor 
    ~Person() { delete [] name; }

    // Implement Copy Semantics
    Person(Person const& other)
        : name(new char[std::strlen(other.name) + 1])
        , age(other.age)
    {
        std::strcpy(name, other.name);
    }
    
    Person &operator=(Person const& other) 
    {
        // Use copy and swap idiom to implement assignment.
        Person copy(other);
        swap(*this, copy);
        return *this;
    }

    // Implement Move Semantics
    // Note: It is usually best to mark move operators as noexcept
    //       This allows certain optimizations in the standard library
    //       when the class is used in a container.

    Person(Person&& that) noexcept
        : name(nullptr)               // Set the state so we know it is undefined
        , age(0)
    {
        swap(*this, that);
    }

    Person& operator=(Person&& that) noexcept
    {
        swap(*this, that);
        return *this;
    }

    friend void swap(Person& lhs, Person& rhs) noexcept
    {
        std::swap(lhs.name, rhs.name);
        std::swap(lhs.age, rhs.age);
    }
};

或者,复制和移动赋值运算符都可以用单个赋值运算符替换,它通过值而不是引用或右值引用来获取实例,以便于使用复制和交换习惯用法。

Person& operator=(Person copy)
{
    swap(*this, copy);
    return *this;
}

三规则延伸到五规则对于性能原因很重要,但在大多数情况下并非绝对必要。添加复制构造函数和赋值运算符可确保移动类型不会泄漏内存(在这种情况下,移动构造将简单地回退到复制),但将执行调用者可能没有预料到的副本。