La Regla de los Tres (también conocida como la "Ley de los Tres Grandes" o "Tres Grandes") es una regla de C++ que dice que si una clase o estructura define uno de los siguientes métodos, debe definir explícitamente los tres métodos [1 ] :
Estos tres métodos son funciones miembro especiales que el compilador crea automáticamente si el programador no las declara explícitamente. Si uno de ellos debe ser definido por el programador, esto significa que la versión generada por el compilador no satisface las necesidades de la clase en un caso, y probablemente no lo hará en otros casos.
Una enmienda a esta regla es que si se utiliza RAII (del inglés Resource Acquisition Is Initialization ), el destructor utilizado puede permanecer sin definir (a veces denominado "Ley de los dos grandes") [2] .
Dado que los constructores y operadores de asignación definidos implícitamente simplemente copian todos los miembros de datos de una clase [3] , es necesario definir constructores de copia y operadores de asignación de copia explícitos en los casos en que una clase encapsula estructuras de datos complejas o puede admitir acceso exclusivo a recursos. Y también en los casos en que la clase contenga datos constantes o referencias.
Con el lanzamiento del undécimo estándar , la regla se expandió y se conoció como la regla de cinco. Ahora, al implementar el constructor, debe implementar:
Ejemplo de regla de cinco:
#incluir <ccadena> clase RFive { privado : char * ccadena ; público : // Constructor con lista de inicialización y cuerpo RFive ( const char * arg ) : cstring ( nuevo carácter [ estándar :: strlen ( arg ) + 1 ]) { std :: strcpy ( cstring , arg ); } // Destructor ~ RFive () { eliminar [] cstring ; } // Copiar constructor RFive ( const RFive y otros ) { cstring = new char [ std :: strlen ( otro . cstring ) + 1 ]; std :: strcpy ( cstring , otro . cstring ); } // Mover constructor, noexcept - para optimización cuando se usan contenedores estándar RFive ( RFive && otro ) no excepto { cstring = otro . ccadena ; otro _ cstring = nullptr ; } // Copiar operador de asignación RFive & operador = ( const RFive & otro ) { si ( este == y otros ) devolver * esto ; char * tmp_cstring = new char [ std :: strlen ( otro . cstring ) + 1 ]; std :: strcpy ( tmp_cstring , otro . cstring ); eliminar [] cstring ; cstring = tmp_cstring ; devolver * esto ; } // Mover operador de asignación RFive & operador = ( RFive && otro ) no excepto { si ( este == y otros ) devolver * esto ; eliminar [] cstring ; cstring = otro . ccadena ; otro _ cstring = nullptr ; devolver * esto ; } // También puede reemplazar ambas declaraciones de asignación con la siguiente declaración // RFive& operator=(RFive other) // { // std::swap(cstring, otro.cstring); // devuelve *esto; // } };Siempre debes evitar duplicar el mismo código, porque si cambias o arreglas una sección, tendrás que acordarte de arreglar el resto. El idioma de copiar e intercambiar le permite evitar esto al reutilizar el código del constructor de copia, por lo que para la clase RFive tendrá que crear una función de intercambio amigable e implementar el operador de asignación copiándolo y moviéndolo. Además, con esta implementación, no hay necesidad de verificar la autoasignación.
#incluir <ccadena> clase RFive { // resto del código RFive & operator = ( const RFive & other ) // Operador de asignación de copias { Rcinco tmp ( otro ); intercambiar ( * esto , tmp ); devolver * esto ; } RFive & operator = ( RFive && other ) // Mover operador de asignación { intercambiar ( * esto , otro ); devolver * esto ; } intercambio de anulación de amigos ( RFive & l , RFive & r ) { utilizando std :: intercambio ; intercambio ( l . cstring , r . cstring ); } // resto del código };También es apropiado que los operadores de asignación hagan del valor de retorno una referencia constante: const RFive& operator=(const RFive& other);. La constante adicional evitará que escribamos código ofuscado como este: (a=b=c).foo();.
Martín Fernández también propuso la regla del cero. [5] Según esta regla, no debe definir ninguna de las cinco funciones usted mismo; es necesario confiar su creación al compilador (para asignarles el valor = default;). Para poseer recursos, en lugar de punteros simples, debe usar clases contenedoras especiales, como std::unique_ptry std::shared_ptr. [6]
C++ | |
---|---|
Peculiaridades | |
algunas bibliotecas | |
compiladores | |
influenciado | |
|