Menu
Coddy logo textTech

Construtor de Cópia

Parte da seção Programação Orientada a Objetos do Journey de C++ da Coddy — lição 20 de 104.

Um construtor de cópia cria um novo objeto como uma cópia de um objeto existente. Ele é chamado quando você inicializa um objeto a partir de outro objeto do mesmo tipo, passa um objeto por valor ou retorna um objeto por valor.

O construtor de cópia recebe uma referência const para um objeto da mesma classe:

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

Player original("Hero", 100);
Player copy = original;    // Construtor de cópia chamado
Player another(original);  // Também chama o construtor de cópia

Se você não definir um construtor de cópia, o compilador gera um que realiza uma cópia rasa (shallow copy) - ele copia o valor de cada membro diretamente. Isso funciona bem para tipos simples, mas causa problemas quando sua classe gerencia memória dinâmica.

class Buffer {
    int* data;
    size_t size;
public:
    Buffer(size_t s) : size(s) {
        data = new int[size];
    }
    
    // Construtor de cópia profunda
    Buffer(const Buffer& other) : size(other.size) {
        data = new int[size];              // Aloca nova memória
        for (size_t i = 0; i < size; i++)
            data[i] = other.data[i];       // Copia o conteúdo
    }
    
    ~Buffer() { delete[] data; }
};

Sem um construtor de cópia personalizado, ambos os objetos apontariam para a mesma memória. Quando um é destruído, o outro teria um ponteiro pendente. Uma cópia profunda aloca nova memória e copia os dados reais, garantindo que cada objeto possua seus próprios recursos.

challenge icon

Desafio

Fácil

Vamos construir um sistema de histórico de mensagens que demonstra a importância da cópia profunda (deep copy) quando sua classe gerencia memória dinâmica. Você criará uma classe MessageLog que armazena mensagens em um array alocado dinamicamente e implementará um construtor de cópia adequado para garantir que cada cópia tenha sua própria memória independente.

Você criará dois arquivos para organizar seu código:

  • MessageLog.h: Define uma classe MessageLog que gerencia uma coleção de mensagens usando memória dinâmica. Sua classe deve ter:
    • Membros privados: um ponteiro para um array de strings para armazenar mensagens, uma capacity para o número máximo de mensagens e um count para o número atual de mensagens
    • Um construtor parametrizado que recebe uma capacidade, aloca o array no heap e inicializa o count como 0
    • Um construtor de cópia que realiza uma cópia profunda (deep copy) — alocando nova memória e copiando todas as mensagens do objeto de origem
    • Um destruidor que libera a memória alocada e imprime "MessageLog destroyed"
    • Um método addMessage(string msg) que adiciona uma mensagem se houver espaço
    • Um método getCount() que retorna a contagem atual de mensagens
    • Um método getMessage(int index) que retorna a mensagem no índice fornecido
  • main.cpp: Demonstre que seu construtor de cópia cria uma cópia independente. Leia um valor de capacidade e duas mensagens da entrada, então:
    • Crie um MessageLog chamado original com a capacidade informada
    • Adicione ambas as mensagens ao original
    • Crie uma cópia chamada backup usando o construtor de cópia
    • Adicione uma terceira mensagem "New message" apenas ao original
    • Imprima "Original count: <count>"
    • Imprima "Backup count: <count>"
    • Imprima todas as mensagens do backup, uma por linha

O formato de entrada será:

  • Primeira linha: capacidade (inteiro)
  • Segunda linha: primeira mensagem (string)
  • Terceira linha: segunda mensagem (string)

Se sua cópia profunda for implementada corretamente, o backup deve ter 2 mensagens enquanto o original tem 3 — provando que eles têm memória independente. Sem um construtor de cópia adequado, ambos compartilhariam o mesmo array e as alterações em um afetariam o outro!

Inclua seu arquivo de cabeçalho no main.cpp usando #include "MessageLog.h".

Folha de consulta

Um construtor de cópia cria um novo objeto como uma cópia de um objeto existente. Ele recebe uma referência constante para um objeto da mesma classe:

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

Player original("Hero", 100);
Player copy = original;    // Construtor de cópia chamado
Player another(original);  // Também chama o construtor de cópia

Se você não definir um construtor de cópia, o compilador gera um que realiza uma cópia rasa (shallow copy) - copiando o valor de cada membro diretamente. Isso causa problemas quando sua classe gerencia memória dinâmica.

Um construtor de cópia profunda (deep copy constructor) aloca nova memória e copia os dados reais, garantindo que cada objeto possua seus próprios recursos:

class Buffer {
    int* data;
    size_t size;
public:
    Buffer(size_t s) : size(s) {
        data = new int[size];
    }
    
    // Construtor de cópia profunda
    Buffer(const Buffer& other) : size(other.size) {
        data = new int[size];              // Aloca nova memória
        for (size_t i = 0; i < size; i++)
            data[i] = other.data[i];       // Copia o conteúdo
    }
    
    ~Buffer() { delete[] data; }
};

Sem um construtor de cópia personalizado, ambos os objetos apontariam para a mesma memória, causando ponteiros pendentes (dangling pointers) quando um deles fosse destruído.

Experimente você mesmo

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

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

    // TODO: Criar um MessageLog chamado 'original' com a capacidade de entrada

    // TODO: Adicionar ambas as mensagens ao original

    // TODO: Criar uma cópia chamada 'backup' usando o construtor de cópia

    // TODO: Adicionar "New message" apenas ao original

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

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

    // TODO: Imprimir todas as mensagens do backup, uma por linha

    return 0;
}
quiz iconTeste seus conhecimentos

Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.

Todas as lições de Programação Orientada a Objetos