Menu
Coddy logo textTech

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 toma una referencia const a un objeto de la misma clase:

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 para 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];              // Asignar nueva memoria
        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 destruyera, el otro tendría un puntero colgante. Una copia profunda asigna memoria nueva y copia los datos reales, lo que garantiza que cada objeto sea propietario de sus propios recursos.

challenge icon

Desafío

Fácil

Construyamos 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 un arreglo asignado 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 administre una colección de mensajes mediante memoria dinámica. Tu clase debe tener:
    • Miembros privados: un puntero a un arreglo 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 reciba una capacidad, asigne el arreglo en el heap e inicialice count en 0
    • Un constructor de copia que realice una copia profunda: asignando nueva memoria y copiando todos los mensajes del objeto de origen
    • Un destructor que libere la memoria asignada e imprima "MessageLog destroyed"
    • Un método addMessage(string msg) que agregue un mensaje si hay espacio
    • Un método getCount() que devuelva el recuento actual de mensajes
    • Un método getMessage(int index) que devuelva el mensaje ubicado en el índice indicado
  • 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 introducida
    • Agrega ambos mensajes a original
    • Crea una copia llamada backup usando el constructor de copia
    • Agrega un tercer mensaje "New message" únicamente 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 tu copia profunda está implementada correctamente, la copia de seguridad debe tener 2 mensajes, mientras que el original debe tener 3: esto demuestra que tienen memoria independiente. Sin un constructor de copia adecuado, ambos compartirían el mismo arreglo y los cambios realizados 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: Print "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