O que é o Idioma Copy-and-Swap e por que é indispensável em 2025
Imagine que você está escrevendo uma classe para trabalhar com memória dinâmica. Tudo vai bem, até chegar na sobrecarga do operador de atribuição. Você escreve operator=, verifica a auto-atribuição, libera manualmente os recursos antigos, aloca novos… E em 90% dos casos comete um bug que leva a vazamento de memória ou dupla exclusão. Familiar?
Em C++, existe uma solução elegante — o idioma copy-and-swap. Não é apenas um truque da moda, mas uma técnica fundamental que permite escrever código seguro, resistente a exceções e conciso. Em 2025, quando C++23 já está ativamente em uso e o padrão C++26 está no horizonte, esse idioma continua sendo o padrão ouro para classes que gerenciam recursos.
Neste artigo, vamos analisar o idioma copy-and-swap do início ao fim: começando pelo problema que ele resolve, até exemplos práticos e melhores práticas de 2025. Prepare o compilador — vai ter muito código!
O problema: por que o operator= ingênuo é prejudicial
Vamos começar com um exemplo clássico de uma classe que gerencia um array dinâmico. Sem o idioma copy-and-swap, o código geralmente se parece com isso:
class DumbArray {public: DumbArray(size_t size) : size_(size), data_(new int[size]) {} ~DumbArray() { delete[] data_; } // Operador de atribuição ingênuo DumbArray& operator=(const DumbArray& other) { if (this == &other) return *this; // verificação de auto-atribuição delete[] data_; // libera recursos antigos size_ = other.size_; data_ = new int[size_]; // aloca novos std::copy(other.data_, other.data_ + size_, data_); return *this; } private: size_t size_; int* data_;};À primeira vista, tudo parece lógico. Mas existem três problemas:
- Falta de garantia básica de exceções. Se
new int[size_]lançarstd::bad_alloc, o objeto permanece em um estado inválido:data_apontará para memória excluída. - Duplicação de lógica. A cópia de dados já está implementada no construtor de cópia. Por que estamos escrevendo a mesma coisa manualmente?
- Verificação de auto-atribuição. Ela torna o código mais lento e é facilmente esquecida. Além disso, em 95% dos casos a auto-atribuição não ocorre, mas a verificação ainda é executada.
E isso não é tudo. Se você adicionar outros recursos à classe (como descritores de arquivo ou mutexes), o código se torna não apenas pesado — ele se torna perigoso. É aqui que o idioma copy-and-swap entra em cena.
A essência do idioma: três passos para um operator= ideal
O idioma copy-and-swap é baseado em uma ideia simples: não escreva a lógica de cópia no operador de atribuição — use o construtor de cópia e a função swap. O algoritmo é o seguinte:
- Copy: criamos uma cópia temporária do objeto usando o construtor de cópia.
- Swap: trocamos o conteúdo do objeto atual com a cópia temporária.
- Destroy: o objeto temporário é destruído, liberando os recursos antigos.
No código, fica assim:
class SmartArray {public: SmartArray(size_t size = 0) : size_(size), data_(size ? new int[size] : nullptr) {} // Construtor de cópia SmartArray(const SmartArray& other) : size_(other.size_), data_(other.size_ ? new int[other.size_] : nullptr) { std::copy(other.data_, other.data_ + size_, data_); } // Destrutor ~SmartArray() { delete[] data_; } // Função swap amiga friend void swap(SmartArray& first, SmartArray& second) noexcept { using std::swap; swap(first.size_, second.siz