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 a 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 superficial - ele copia diretamente o valor de cada membro. 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]; // Alocar nova memória
for (size_t i = 0; i < size; i++)
data[i] = other.data[i]; // Copiar conteúdos
}
~Buffer() { delete[] data; }
};Sem um construtor de cópia personalizado, ambos os objetos apontariam para a mesma memória. Quando um deles fosse 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 criar um sistema de histórico de mensagens que demonstra a importância da cópia profunda 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: defina 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 umacountpara o número atual de mensagens - Um construtor parametrizado que recebe uma capacidade, aloca o array no heap e inicializa count com 0
- Um construtor de cópia que realiza uma cópia profunda: alocando nova memória e copiando todas as mensagens do objeto de origem
- Um destrutor 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 quantidade 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 e, então:- Crie um
MessageLogchamadooriginalcom a capacidade informada - Adicione ambas as mensagens a
original - Crie uma cópia chamada
backupusando o construtor de cópia - Adicione uma terceira mensagem
"New message"somente aoriginal - Imprima
"Original count: <count>" - Imprima
"Backup count: <count>" - Imprima todas as mensagens de
backup, uma por linha
- Crie um
O formato da 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 deverá ter 2 mensagens, enquanto o original terá 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 em main.cpp usando #include "MessageLog.h".
Experimente você mesmo
#include <iostream>
#include <string>
#include "MessageLog.h"
using namespace std;
int main() {
// Ler entrada
int capacity;
cin >> capacity;
cin.ignore();
string msg1, msg2;
getline(cin, msg1);
getline(cin, msg2);
// TODO: Crie um MessageLog chamado 'original' com a capacidade de entrada
// TODO: Adicione ambas as mensagens a original
// TODO: Crie uma cópia chamada 'backup' usando o construtor de cópia
// TODO: Adicione "New message" apenas a original
// TODO: Print "Original count: <count>"
// TODO: Imprima "Backup count: <count>"
// TODO: Imprima todas as mensagens de 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 POO
Arquivos externosBuild e compilação em C++Arquivos de cabeçalho e arquivos-fonteNamespaces e escopoIntrodução à POO em C++Classes vs. objetosO ponteiro 'this'Métodos (funções membro)Atributos (membros de dados)Noções básicas de construtores e destruidoresRevisão - calculadora simples4Propriedades de Classe
Membros de Instância vs. EstáticosGetters e SettersFunções Membro ConstPalavra-chave MutableMétodos e Variáveis EstáticosFunções e Classes FriendRevisão - Gerenciador de Contas Bancárias7Herança
Herança básicaNíveis de acesso da herançaOrdem de chamada de construtor e destrutorSobrescrita de métodosFunções virtuais e VTableHerança múltiplaHerança virtualRevisão - Hierarquia de funcionários10Visão geral da STL
Visão geral e filosofia da STLContêineres da STLIteradoresAlgoritmos da STLFunctors e expressões lambdaRecapitulação - Frequência de palavras13Padrões de Projeto — Parte 1
Introdução aos Padrões de ProjetoPadrão SingletonFactory e Abstract FactoryPadrão BuilderPadrão ObserverPadrão Strategy2Gerenciamento de Memória
Memória Stack vs. HeapPonteiros e ReferênciasMemória Dinâmica (new/delete)Ponteiros Inteligentes em C++RAII em C++Revisão - Gerenciador de Array Dinâmico5Encapsulamento
Especificadores de acesso em C++Especificadores de acesso em profundidadeOcultação de informaçõesStruct vs ClasseClasses aninhadas e internasRecapitulação - Sistema de registros de alunos8Polimorfismo
Polimorfismo em compilação vs. execuçãoSobrecarga de funçõesFunções virtuais revisadasFunções virtuais purasClasses abstratasDesign de interfaces em C++Conversão dinâmica e RTTIRecapitulação — Calculadora de formas11Conceitos Avançados de OOP
Composição vs. HerançaMixins via CRTPIdioma PimplApagamento de TiposClasses Enum e Tipagem ForteTratamento de Exceções em OOPHierarquias de Exceções Personalizadas14Padrões de Projeto — Parte 2
Padrão CommandPadrão AdapterPadrão DecoratorPadrão Template MethodPadrão StatePadrão CompositeRAII como Padrão3Construtores e Destrutores
Construtor PadrãoConstrutor ParametrizadoConstrutor de CópiaConstrutor de MovimentoListas de Inicialização do ConstrutorConstrutores DelegantesAprofundamento em DestrutoresRegra dos Três / Cinco / ZeroRevisão - Classe String6Sobrecarga de Operadores
Introdução à Sobrecarga de OperadoresSobrecarga de Operadores AritméticosSobrecarga de Operadores de ComparaçãoOperadores de FluxoSobrecarga do Operador de AtribuiçãoSobrecarga dos Operadores [] e ()Operadores de Conversão de TipoRecapitulação - Classe Matrix9Templates
Templates de FunçõesTemplates de ClassesEspecialização de TemplatesTemplates VariádicosNoções Básicas de SFINAE e Traits de TiposRecapitulação - Contêiner GenéricoPratique por conta própria: Compilador de C++ online