Construtor de Movimento
Parte da seção Programação Orientada a Objetos do Journey de C++ da Coddy. Lição 21 de 104.
Um construtor de movimento transfere recursos de um objeto temporário em vez de copiá-los. Enquanto um construtor de cópia duplica dados, um construtor de movimento "rouba" os recursos, deixando o objeto de origem em um estado válido, porém vazio. Isso evita cópias profundas (deep copies) caras quando o objeto de origem está prestes a ser destruído de qualquer forma.
O construtor de movimento recebe uma referência rvalue, denotada por &&:
class Buffer {
int* data;
size_t size;
public:
Buffer(size_t s) : size(s), data(new int[s]) {}
// Construtor de movimento
Buffer(Buffer&& other) noexcept {
data = other.data; // Rouba o ponteiro
size = other.size;
other.data = nullptr; // Deixa a origem em um estado válido
other.size = 0;
}
~Buffer() { delete[] data; }
};A principal diferença em relação à cópia: não alocamos nova memória. Simplesmente assumimos a propriedade da memória existente e definimos o ponteiro da origem como nullptr para que o destrutor dela não exclua nossos dados.
Construtores de movimento são chamados ao inicializar a partir de objetos temporários ou ao usar std::move():
Buffer createBuffer() {
return Buffer(1000); // Construtor de movimento usado (valor de retorno)
}
Buffer b1(1000);
Buffer b2(std::move(b1)); // Movimentação explícita - b1 agora está vazioMarque os construtores de movimento como noexcept quando possível. Isso informa ao compilador que a operação não lançará exceções, permitindo otimizações importantes em contêineres como std::vector.
Desafio
FácilVamos criar um sistema de pacotes de dados que demonstra como construtores de movimento transferem eficientemente a propriedade de recursos. Você criará uma classe DataPacket que gerencia um array de bytes alocado dinamicamente e implementará um construtor de movimento que “rouba” os dados em vez de copiá-los.
Você criará dois arquivos para organizar seu código:
DataPacket.h: Defina uma classeDataPacketque gerencie um payload de dados inteiros. Sua classe deve ter:- Membros privados: um ponteiro para um array de inteiros (
payload), umsizepara o número de elementos e umpacketId(string) para identificar o pacote - Um construtor parametrizado que receba um ID e um tamanho de pacote, aloque o array e o preencha com valores de 0 a size-1. Imprima
"Packet <id> created with size <size>" - Um construtor de movimento marcado como
noexceptque transfira a propriedade do payload do objeto de origem. Imprima"Packet <id> moved". Lembre-se de deixar a origem em um estado vazio válido (nullptr, size 0) - Um destrutor que libere a memória se o ponteiro não for nulo e imprima
"Packet <id> destroyed" - Um método
getSize()que retorne o tamanho atual - Um método
getId()que retorne o ID do pacote - Um método
getSum()que retorne a soma de todos os elementos do payload (retorne 0 se o payload for nulo)
- Membros privados: um ponteiro para um array de inteiros (
main.cpp: Demonstre a construção por movimento lendo um ID e um tamanho de pacote da entrada. Em seguida:- Crie um
DataPacketchamadooriginalcom os valores de entrada - Imprima
"Original - ID: <id>, Size: <size>, Sum: <sum>" - Crie um novo pacote chamado
transferredmovendo a partir deoriginalusandostd::move() - Imprima
"After move:" - Imprima
"Original - ID: <id>, Size: <size>, Sum: <sum>" - Imprima
"Transferred - ID: <id>, Size: <size>, Sum: <sum>"
- Crie um
O formato da entrada será:
- Primeira linha: ID do pacote (string)
- Segunda linha: tamanho (inteiro)
Após o movimento, o pacote original deve ter tamanho 0 e soma 0 (pois seus dados foram transferidos), enquanto o pacote transferido deve ter todos os dados originais. Isso demonstra como a semântica de movimento evita cópias profundas dispendiosas simplesmente transferindo a propriedade do ponteiro.
Inclua seu arquivo de cabeçalho em main.cpp usando #include "DataPacket.h" e não se esqueça de incluir <utility> para std::move().
Experimente você mesmo
#include <iostream>
#include <string>
#include <utility>
#include "DataPacket.h"
using namespace std;
int main() {
// Ler entrada
string packetId;
int size;
cin >> packetId;
cin >> size;
// TODO: Criar um DataPacket chamado 'original' com os valores de entrada
// TODO: Imprimir "Original - ID: <id>, Size: <size>, Sum: <sum>"
// TODO: Criar um novo pacote chamado 'transferred' movendo do original
// Dica: Use std::move()
// TODO: Imprimir "After move:"
// TODO: Imprimir "Original - ID: <id>, Size: <size>, Sum: <sum>"
// TODO: Imprimir "Transferred - ID: <id>, Size: <size>, Sum: <sum>"
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