Menu
CoddyTech

Constructor de copia

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

Un constructor de copia crea un objeto nuevo como copia de un objeto existente. Se llama cuando inicializas un objeto a partir de otro objeto del mismo tipo, pasas un objeto por valor o devuelves un objeto por valor.

El constructor de copia recibe una referencia constante a un objeto de la misma clase. Debe ser una referencia: pasar other por valor requeriría a su vez una copia, lo que llamaría al constructor de copia una y otra vez para siempre. Es const porque hacer una copia nunca debería cambiar el original:

class Player {
    std::string name;
    int health;
public:
    Player(std::string n, int h) : name(n), health(h) {}
    
    // Constructor de copia
    Player(const Player& other) {
        name = other.name;
        health = other.health;
    }
};

Player original("Hero", 100);
Player copy = original;    // Se llama al constructor de copia
Player another(original);  // También llama al constructor de copia

Si no defines un constructor de copia, el compilador genera uno que realiza una copia superficial: copia directamente el valor de cada miembro. Esto funciona bien con tipos simples, pero causa problemas cuando tu clase administra memoria dinámica.

class Buffer {
    int* data;
    size_t size;
public:
    Buffer(size_t s) : size(s) {
        data = new int[size];
    }
    
    // Constructor de copia profunda
    Buffer(const Buffer& other) : size(other.size) {
        data = new int[size];              // Reservar memoria nueva
        for (size_t i = 0; i < size; i++)
            data[i] = other.data[i];       // Copiar el contenido
    }
    
    ~Buffer() { delete[] data; }
};

Sin un constructor de copia personalizado, ambos objetos apuntarían a la misma memoria. Cuando uno se destruye, el otro tendría un puntero colgante. Una copia profunda asigna nueva memoria y copia los datos reales, lo que garantiza que cada objeto sea propietario de sus propios recursos.

challenge icon

Desafío

Fácil

Vamos a crear un sistema de historial de mensajes que demuestre la importancia de la copia profunda cuando tu clase administra memoria dinámica. Crearás una clase MessageLog que almacena mensajes en una matriz asignada dinámicamente e implementarás un constructor de copia adecuado para garantizar que cada copia tenga su propia memoria independiente.

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

  • MessageLog.h: Define una clase MessageLog que administra una colección de mensajes usando memoria dinámica. Tu clase debe tener:
    • Miembros privados: un puntero a una matriz de cadenas para almacenar mensajes, una capacity para el número máximo de mensajes y un count para el número actual de mensajes
    • Un constructor parametrizado que recibe una capacidad, asigna la matriz en el heap e inicializa count en 0
    • Un constructor de copia que realiza una copia profunda: asigna memoria nueva y copia todos los mensajes del objeto de origen
    • Un destructor que libera la memoria asignada e imprime "MessageLog destroyed"
    • Un método addMessage(string msg) que añade un mensaje si hay espacio
    • Un método getCount() que devuelve el recuento actual de mensajes
    • Un método getMessage(int index) que devuelve el mensaje en el índice dado
  • main.cpp: Demuestra que tu constructor de copia crea una copia independiente. Lee un valor de capacidad y dos mensajes de la entrada, y después:
    • Crea un MessageLog llamado original con la capacidad de entrada
    • Añade ambos mensajes a original
    • Crea una copia llamada backup usando el constructor de copia
    • Añade un tercer mensaje "New message" solo a original
    • Imprime "Original count: <count>"
    • Imprime "Backup count: <count>"
    • Imprime todos los mensajes de backup, uno por línea

El formato de entrada será:

  • Primera línea: capacidad (entero)
  • Segunda línea: primer mensaje (cadena)
  • Tercera línea: segundo mensaje (cadena)

Si implementas correctamente la copia profunda, la copia de seguridad tendrá 2 mensajes mientras que el original tendrá 3: esto demuestra que tienen memoria independiente. Sin un constructor de copia adecuado, ambos compartirían la misma matriz y los cambios en uno afectarían al otro.

Incluye tu archivo de encabezado en main.cpp usando #include "MessageLog.h".

Pruébalo tú mismo

#include <iostream>
#include <string>
#include "MessageLog.h"
using namespace std;

int main() {
    // Lee la entrada
    int capacity;
    cin >> capacity;
    cin.ignore();
    
    string msg1, msg2;
    getline(cin, msg1);
    getline(cin, msg2);

    // TODO: Crea un MessageLog llamado 'original' con la capacidad de entrada

    // TODO: Añade ambos mensajes a original

    // TODO: Crea una copia llamada 'backup' usando el constructor de copia

    // TODO: Añade "New message" solo a original

    // TODO: Imprime "Original count: <count>"

    // TODO: Imprime "Backup count: <count>"

    // TODO: Imprime todos los mensajes de backup, uno por línea

    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