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".
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