C++ Regola del Cinque


Esempio

C ++ 11

C ++ 11 introduce due nuove funzioni membro speciali: il costruttore di movimento e l'operatore di assegnazione del movimento. Per tutti gli stessi motivi per cui vuoi seguire la Regola dei Tre in C ++ 03, di solito vuoi seguire la Regola dei Cinque in C ++ 11: Se una classe richiede UNA delle cinque funzioni membro speciali, e se muove la semantica sono desiderati, quindi molto probabilmente richiede TUTTI i CINQUE di loro.

Nota, tuttavia, che non seguire la Regola dei Cinque non viene considerato un errore, ma un'opportunità di ottimizzazione persa, purché la Regola del Tre sia ancora seguita. Se nessun costruttore di movimento o operatore di assegnazione movimento è disponibile quando il compilatore normalmente ne usa uno, utilizzerà semantica di copia, se possibile, risultando in un'operazione meno efficiente a causa di operazioni di copia non necessarie. Se non si desidera spostare la semantica per una classe, non è necessario dichiarare un costruttore di movimenti o un operatore di assegnazione.

Lo stesso esempio della regola del tre:

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);
    }
};

In alternativa, sia l'operatore di assegnazione di copia che di spostamento può essere sostituito con un singolo operatore di assegnazione, che prende un'istanza in base al valore anziché al riferimento o al riferimento di rvalue per facilitare l'uso dell'idioma copy-and-swap.

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

Estendere dalla Regola dei Tre alla Regola dei Cinque è importante per i motivi di prestazione, ma nella maggior parte dei casi non è strettamente necessario. L'aggiunta del costruttore di copia e dell'operatore di assegnazione garantisce che lo spostamento del tipo non crei memoria (la costruzione delle mosse ricadrà semplicemente sulla copia in quel caso), ma eseguirà copie che il chiamante probabilmente non ha previsto.