Menu
Coddy logo textTech

Regla de tres / cinco / cero

Parte de la sección Programación Orientada a Objetos del Journey de C++ de Coddy. Lección 25 de 104.

Cuando tu clase administra recursos como la memoria dinámica, has aprendido que necesitas un destructor personalizado, un constructor de copia y un constructor de movimiento. Pero existe un principio rector que te ayuda a decidir qué funciones miembro especiales implementar: la Regla de Tres, Cinco y Cero.

La Regla de los tres establece: si defines cualquiera de estos tres, deberías definir los tres:

  • Destructor
  • Constructor de copia
  • Operador de asignación de copia

La Regla de los cinco amplía esto para el C++ moderno, añadiendo operaciones de movimiento:

  • Destructor
  • Constructor de copia
  • Operador de asignación de copia
  • Constructor de movimiento
  • Operador de asignación de movimiento
class Buffer {
    int* data;
    size_t size;
public:
    Buffer(size_t s) : size(s), data(new int[s]) {}
    
    ~Buffer() { delete[] data; }                          // 1. Destructor
    Buffer(const Buffer& other);                          // 2. Constructor de copia
    Buffer& operator=(const Buffer& other);               // 3. Asignación de copia
    Buffer(Buffer&& other) noexcept;                      // 4. Constructor de movimiento
    Buffer& operator=(Buffer&& other) noexcept;           // 5. Asignación de movimiento
};

La Regla del Cero es el enfoque más sencillo: si tu clase no gestiona recursos directamente, no definas ninguna de estas funciones. Deja que el compilador las genere o utiliza punteros inteligentes y contenedores estándar que gestionen los recursos por ti.

class Player {
    std::string name;              // std::string gestiona su propia memoria
    std::vector<int> scores;       // std::vector maneja sus recursos
public:
    Player(std::string n) : name(n) {}
    // ¡No se necesitan destructor, copy ni move!
};

Seguir estas reglas evita errores como la doble eliminación, las fugas de memoria y los punteros colgantes que ocurren cuando algunas operaciones están definidas, pero faltan otras.

challenge icon

Desafío

Fácil

Construyamos una clase TextBuffer que siga la Regla de Cinco: implementando las cinco funciones miembro especiales para gestionar correctamente datos de caracteres asignados dinámicamente. Esto demostrará cómo las operaciones de copia y movimiento trabajan juntas para crear una clase robusta que gestiona recursos.

Crearás dos archivos para organizar tu código:

  • TextBuffer.h: Define una clase TextBuffer que almacena texto en un arreglo de caracteres asignado dinámicamente. Tu clase necesita:
    • Miembros privados: un puntero char* llamado data para el contenido del texto, y un size_t length para la longitud de la cadena (sin incluir el terminador nulo)
    • Un constructor parametrizado que recibe una cadena C (const char*), asigna memoria, copia el contenido e imprime "TextBuffer created: <text>"
    • Un destructor que libera la memoria (si no es nula) e imprime "TextBuffer destroyed"
    • Un constructor de copia que realiza una copia profunda e imprime "TextBuffer copied"
    • Un operador de asignación por copia que gestiona la autoasignación, limpia los datos existentes, realiza una copia profunda e imprime "TextBuffer copy-assigned". Devuelve *this
    • Un constructor de movimiento (marcado como noexcept) que transfiere la propiedad e imprime "TextBuffer moved". Deja el origen en un estado vacío válido
    • Un operador de asignación por movimiento (marcado como noexcept) que gestiona la autoasignación, limpia los datos existentes, transfiere la propiedad e imprime "TextBuffer move-assigned". Devuelve *this
    • Un método getText() que devuelve el texto almacenado (devuelve una cadena vacía "" si data es nulo)
    • Un método getLength() que devuelve la longitud
  • main.cpp: Demuestra las cinco funciones miembro especiales en acción. Lee una cadena de texto de la entrada y después:
    • Crea un TextBuffer llamado original con el texto de entrada
    • Crea copied usando el constructor de copia a partir de original
    • Crea another con el texto "Temporary"
    • Usa la asignación por copia: another = original
    • Crea moved mediante construcción por movimiento a partir de original usando std::move()
    • Crea target con el texto "Target"
    • Usa la asignación por movimiento: target = std::move(copied)
    • Imprime "--- Final State ---"
    • Imprime "original: <text> (length: <len>)" para cada búfer: original, copied, moved, another, target

Después de los movimientos, los objetos de origen (original y copied) deberían mostrar un texto vacío con longitud 0, mientras que los objetos de destino contienen los datos transferidos. Esto demuestra la Regla de Cinco en acción: las cinco funciones trabajan juntas para garantizar una gestión segura de los recursos.

Incluye <cstring> para funciones de cadenas como strlen y strcpy, y <utility> para std::move().

Pruébalo tú mismo

#include <iostream>
#include <string>
#include <utility>
#include "TextBuffer.h"

using namespace std;

int main() {
    string input;
    getline(cin, input);

    // TODO: Crea un TextBuffer llamado 'original' con el texto de entrada

    // TODO: Crea 'copied' usando el constructor de copia de 'original'

    // TODO: Crea 'another' con el texto "Temporary"

    // TODO: Usa la asignación por copia: another = original

    // TODO: Crea 'moved' mediante construcción por movimiento desde 'original' usando std::move()

    // TODO: Crea 'target' con el texto "Target"

    // TODO: Usa la asignación por movimiento: target = std::move(copied)

    // TODO: Imprime "--- Final State ---"

    // TODO: Imprime el estado de cada buffer en este formato:
    // "original: <text> (length: <len>)"
    // Imprime para: original, copied, moved, another, target

    return 0;
}
quiz iconPonte a prueba

Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.

Todas las lecciones de Programación Orientada a Objetos

Practica por tu cuenta: Compilador de C++ online