La regla de tres en C++: lo que todo desarrollador debe saber

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

¿Qué es la Regla de Tres en C++? Analicémosla con ejemplos



Si alguna vez has escrito una clase en C++ que gestiona memoria dinámica (por ejemplo, que contiene new o malloc), seguramente te has encontrado con la Regla de tres (Rule of Three). Es una de las reglas más fundamentales del lenguaje, que evita fugas de memoria, dobles eliminaciones y comportamiento indefinido. Analicémosla con ejemplos reales para que no solo memorices la regla, sino que entiendas por qué funciona.



Imagina: has escrito una clase que asigna memoria en el constructor. El compilador amablemente genera por ti el constructor de copia y el operador de asignación. Pero lo hace de forma superficial (shallow copy). Y ahí empiezan los problemas. La regla de tres dice: si tu clase define explícitamente un destructor, un constructor de copia o un operador de asignación, lo más probable es que necesites definir los tres. ¿Por qué? Porque es una señal de que tu clase gestiona algún recurso (memoria, archivo, conexión de red) y la copia debe ser profunda.



El problema: copia superficial (shallow copy)

Veamos un ejemplo típico. La clase String, que almacena una cadena C en memoria dinámica:



class String {private:    char* data;    size_t size;public:    // Constructor    String(const char* str) {        size = strlen(str);        data = new char[size + 1];        strcpy(data, str);    }

// Destructor ~String() { delete[] data; }

// Constructor de copia — generado por el compilador (superficial) // Operador de asignación — generado por el compilador (superficial)

void print() const { std::cout << data << std::endl; }};


Todo parece bien, hasta que intentamos copiar el objeto:



int main() {    String s1("Hello");    String s2 = s1;  // Llamada al constructor de copia (generado)    s1.print();    s2.print();    // ¡El programa fallará con un error de double free!    return 0;}


¿Qué ha ocurrido? El compilador generó un constructor de copia que simplemente copió el puntero data. Ahora s1.data y s2.data apuntan al mismo bloque de memoria. Cuando los objetos salen del ámbito, se llaman a sus destructores. El primer destructor libera la memoria, el segundo intenta liberarla de nuevo — double free. El programa falla o se comporta de manera impredecible.



Solución: La Regla de tres en acción

Para solucionar la situación, es necesario implementar las tres funciones miembro especiales. Esto es la Regla de tres. Escribamos la versión correcta de la clase:



class String {private:    char* data;    size_t size;public:    // Constructor    String(const char* str) {        size = strlen(str);        data = new char[size + 1];        strcpy(data, str);    }

// Destructor ~String() { delete[] data; }

// Constructor de copia — copia profunda String(const String& other) { size = other.size; data = new char[size + 1]; strcpy(data, other.data); }

// Operador de asignación — copia profunda String& operator=(const String& other) { if (this == &other) return *this; // protección contra autoasignación

delete[] data; // liberamos los recursos antiguos

size = other.size; data = new char[size + 1]; strcpy(data, other.data);

return *this; }

void print() const { std::cout << data << std::endl; }};


Ahora la copia funciona correctamente: cada objeto posee su propio bloque de memoria. La asignación también funciona sin

Blogs

Book Recommendations