C++ Memória Intermediário
Cópia e movimento
Em C++, quando um objeto é criado a partir de outro, o compilador decide entre copiar (duplicar recursos) ou mover (transferir recursos). A cópia é feita pelo construtor de cópia, que recebe const T&. O movimento é feito pelo construtor de movimento, que recebe T&& e pode "roubar" os recursos do objeto original, deixando-o em estado válido mas vazio.
A função std::move não move nada: ela apenas converte um lvalue em rvalue, permitindo que o construtor de movimento seja chamado. É um cast para T&&.
A regra dos 5 diz que se você define destrutor, construtor de cópia, atribuição de cópia, construtor de movimento ou atribuição de movimento, provavelmente precisa definir todos os cinco para gerenciar recursos corretamente.
Neste card, vamos criar uma classe que gerencia um buffer dinâmico e implementar cópia e movimento para ver a diferença na prática.
Pontos-chave
- O construtor de cópia (
T(const T&)) duplica os recursos do objeto original. - O construtor de movimento (
T(T&&)) transfere a posse dos recursos, deixando o original vazio. std::moveapenas converte para rvalue; não move nada por si só.- A regra dos 5: se precisar de um dos cinco, provavelmente precisa dos cinco.
- Objetos movidos ficam em estado válido, mas não especificado; não os use após mover.
#include <iostream>
#include <utility>
#include <cstring>
class Buffer {
char* data_;
size_t size_;
public:
// Construtor comum
explicit Buffer(size_t size) : data_(new char[size]), size_(size) {
std::memset(data_, '\0', size_);
std::cout << "Construtor: " << size_ << " bytes\n";
}
// Construtor de cópia: duplica os dados
Buffer(const Buffer& other) : data_(new char[other.size_]), size_(other.size_) {
std::memcpy(data_, other.data_, size_);
std::cout << "Cópia: " << size_ << " bytes\n";
}
// Construtor de movimento: rouba os dados
Buffer(Buffer&& other) noexcept : data_(other.data_), size_(other.size_) {
other.data_ = nullptr;
other.size_ = 0;
std::cout << "Movimento: " << size_ << " bytes\n";
}
// Destrutor
~Buffer() {
delete[] data_;
std::cout << "Destrutor: " << size_ << " bytes\n";
}
// Atribuição de cópia
Buffer& operator=(const Buffer& other) {
if (this != &other) {
delete[] data_;
size_ = other.size_;
data_ = new char[size_];
std::memcpy(data_, other.data_, size_);
}
std::cout << "Atribuição de cópia\n";
return *this;
}
// Atribuição de movimento
Buffer& operator=(Buffer&& other) noexcept {
if (this != &other) {
delete[] data_;
data_ = other.data_;
size_ = other.size_;
other.data_ = nullptr;
other.size_ = 0;
}
std::cout << "Atribuição de movimento\n";
return *this;
}
};
Buffer criarBuffer(size_t n) {
return Buffer(n); // RVO / movimento
}
int main() {
Buffer a(10);
Buffer b = a; // cópia
Buffer c = std::move(a); // movimento
Buffer d(20);
d = criarBuffer(30); // movimento por atribuição
return 0;
}