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ópiaSe 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.
Desafio
FácilVamos 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 classeMessageLogque 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
capacitypara o número máximo de mensagens e umcountpara 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
- Membros privados: um ponteiro para um array de strings para armazenar mensagens, uma
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
MessageLogchamadooriginalcom a capacidade informada - Adicione ambas as mensagens ao
original - Crie uma cópia chamada
backupusando o construtor de cópia - Adicione uma terceira mensagem
"New message"apenas aooriginal - Imprima
"Original count: <count>" - Imprima
"Backup count: <count>" - Imprima todas as mensagens do
backup, uma por linha
- Crie um
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ópiaSe 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;
}
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
1Fundamentos de OOP
Arquivos ExternosBuild e Compilação em C++Arquivos Header e SourceNamespaces e EscopoIntrodução a OOP em C++Classes vs ObjetosO Ponteiro 'this'Métodos (Funções Membro)Atributos (Membros de Dados)Básico de Ctors e DtorsRecapitulação - Calculadora Simples4Propriedades de Classe
Membros de Instância vs EstáticosGetters e SettersFunções de Membro ConstPalavra-chave MutableMétodos e Variáveis EstáticosFunções e Classes FriendRecapitulação - Gerenciador de Conta Bancária7Herança
Herança BásicaNíveis de Acesso na HerançaOrdem de Chamada de Ctor e DtorSobrescrita de MétodosFunções Virtuais e VTableHerança MúltiplaHerança VirtualRecapitulação - Hierarquia de Funcionários2Gerenciamento de Memória
Memória Stack vs HeapPonteiros e ReferênciasMemória Dinâmica (new/delete)Smart Pointers em C++RAII em C++Recapitulação - Gerenciador de Array Dinâmico5Encapsulamento
Especificadores de Acesso em C++Especificadores de Acesso em DetalhesOcultação de InformaçãoStruct vs ClassClasses Aninhadas e InternasRecapitulação - Sistema de Registros de Alunos8Polimorfismo
Polimorfismo de Compilação vs Tempo de ExecuçãoSobrecarga de FunçõesFunções Virtuais RevisitadasFunções Virtuais PurasClasses AbstratasDesign de Interfaces em C++Dynamic Casting e RTTIRecapitulação - Calculadora de Formas3Construtores e Destrutores
Construtor PadrãoConstrutor ParametrizadoConstrutor de CópiaConstrutor de MovimentaçãoListas de Inicialização do ConstrutorConstrutores DelegadosMergulho Profundo em DestrutoresRegra dos Três / Cinco / ZeroRecapitulação - Classe String6Sobrecarga de Operadores
Introdução à Sobrecarga de OperadoresSobrecarga de Operadores AritméticosSobrecarga de Operadores de ComparaçãoOperadores de StreamSobrecarga do Operador de AtribuiçãoSobrecarga dos Operadores [] e ()Operadores de Conversão de TipoRecapitulação - Classe Matrix9Templates
Templates de FunçãoTemplates de ClasseEspecialização de TemplatesTemplates VariádicosFundamentos de SFINAE e Type TraitsRecapitulação - Container Genérico