Regula din trei (C++)

Versiunea actuală a paginii nu a fost încă examinată de colaboratori experimentați și poate diferi semnificativ de versiunea revizuită pe 7 aprilie 2022; verificările necesită 2 modificări .

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.

Regula celor cinci

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

Copiați și partajați limbajul

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();.

Regula zero

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]

Link -uri

  1. Bjarne Stroustrup . Limbajul de programare C++  (neopr.) . - 3. - Addison-Wesley , 2000. - S. 283-284. - ISBN 978-0201700732 .
  2. Karlsson, Bjorn; Wilson, Matthew. Legea celor doi Mari . Sursa C++ . Artima (1 octombrie 2004). Data accesului: 22 ianuarie 2008. Arhivat din original la 17 martie 2012.
  3. Limbajul de programare C++  . - S. 271.
  4. Mutați operatorul de atribuire . En.CPPReference.com . Data accesului: 22 decembrie 2014. Arhivat din original pe 23 decembrie 2014.
  5. Regula lui Zero . Zona de pericol în flăcări . Preluat la 29 iulie 2015. Arhivat din original la 29 august 2015.
  6. Kulikov Alexandru. Regula 3, 5, 0 Habrahabr . Consultat la 14 februarie 2016. Arhivat din original pe 22 februarie 2016.