Regula celor trei (cunoscută și ca „Legea celor trei mari” sau „Trei mari”) este o regulă C++ care spune că, dacă o clasă sau o structură definește una dintre următoarele metode, trebuie să definească în mod explicit toate cele trei metode [1] ] :
Aceste trei metode sunt funcții membre speciale care sunt create automat de compilator dacă nu sunt declarate explicit de programator. Dacă una dintre ele trebuie să fie definită de programator, atunci aceasta înseamnă că versiunea generată de compilator nu satisface nevoile clasei într-un caz și probabil că nu va satisface în alte cazuri.
Un amendament la această regulă este că, dacă se folosește RAII (din engleză. Achiziția resurselor este inițializarea ), atunci destructorul folosit poate rămâne nedefinit (uneori denumit „Legea celor doi mari”) [2] .
Deoarece constructorii și operatorii de atribuire definiți implicit copiază pur și simplu toți membrii de date ai unei clase [3] , definirea constructorilor de copiere explicită și a operatorilor de atribuire a copiei este necesară în cazurile în care o clasă încapsulează structuri complexe de date sau poate suporta acces exclusiv la resurse. Și, de asemenea, în cazurile în care clasa conține date sau referințe constante.
Odată cu lansarea celui de-al unsprezecelea standard , regula sa extins și a devenit cunoscută sub numele de regula celor cinci. Acum, atunci când implementați constructorul, trebuie să implementați:
Exemplu cu regula din cinci:
#include <cstring> clasa RFive { privat : char * cstring ; public : // Constructor cu lista de inițializare și corp RFive ( const char * arg ) : cstring ( caracter nou [ std :: strlen ( arg ) + 1 ]) { std :: strcpy ( cstring , arg ); } // Destructor ~ RFcinc () { șterge [] cstring ; } // Copiați constructorul RFive ( const RFive și altele ) { cstring = new char [ std :: strlen ( other . cstring ) + 1 ]; std :: strcpy ( cstring , other . cstring ); } // Mută constructorul, noexcept - pentru optimizare când se utilizează containere standard RFive ( RFive && altele ) nu , cu excepția { cstring = alt . cstring ; altele . cstring = nullptr ; } // Copiere operator de atribuire RFive și operator = ( const RFive și altele ) { dacă ( acesta == și altele ) returneaza * asta ; char * tmp_cstring = new char [ std :: strlen ( other . cstring ) + 1 ]; std :: strcpy ( tmp_cstring , other . cstring ); șterge [] cstring ; cstring = tmp_cstring ; returneaza * asta ; } // Mută operatorul de atribuire RFive & operator = ( RFive && altele ) noexcept { dacă ( acesta == și altele ) returneaza * asta ; șterge [] cstring ; cstring = alt . cstring ; altele . cstring = nullptr ; returneaza * asta ; } // De asemenea, puteți înlocui ambele instrucțiuni de atribuire cu următoarea instrucțiune // RFive& operator=(RFive altele) // { // std::swap(cstring, other.cstring); // returnează *aceasta; // } };Ar trebui să evitați întotdeauna duplicarea aceluiași cod, deoarece dacă modificați sau reparați o secțiune, va trebui să vă amintiți să remediați restul. Idioma de copiere și schimb vă permite să evitați acest lucru prin reutilizarea codului constructorului de copiere, așa că pentru clasa RFive va trebui să creați o funcție de schimb prietenoasă și să implementați operatorul de atribuire prin copierea și deplasarea prin el. Mai mult, cu această implementare, nu este nevoie să verificați auto-atribuirea.
#include <cstring> clasa RFive { // restul codului RFive & operator = ( const RFive & other ) // Copiați operatorul de atribuire { Rfive tmp ( altul ); swap ( * asta , tmp ); returneaza * asta ; } RFive & operator = ( RFive && other ) // Mută operatorul de atribuire { swap ( * asta , altele ); returneaza * asta ; } friend void swap ( RFive & l , RFive & r ) { folosind std :: swap ; swap ( l . cstring , r . cstring ); } // restul codului };De asemenea, este potrivit ca operatorii de atribuire să facă din valoarea returnată o referință constantă: const RFive& operator=(const RFive& other);. Const extra ne va împiedica să scriem cod ofuscat astfel: (a=b=c).foo();.
Martin Fernandez a propus și regula zero. [5] Conform acestei reguli, nu ar trebui să definiți singur niciuna dintre cele cinci funcții; este necesar să se încredințeze compilatorului crearea lor (să le atribuie valoarea = default;). Pentru a deține resurse, în loc de simple pointeri, ar trebui să utilizați clase speciale de wrapper, cum ar fi std::unique_ptrși std::shared_ptr. [6]
C++ | |
---|---|
Particularități | |
Unele biblioteci | |
Compilatoare | |
influențat | |
|