Regra dos Três em C++: o que todo desenvolvedor deve saber

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

O que é a Regra dos Três em C++? Analisando com exemplos



Se você já escreveu em C++ uma classe que gerencia memória dinâmica (por exemplo, contém new ou malloc), certamente já se deparou com a Regra dos Três (Rule of Three). Esta é uma das regras mais fundamentais da linguagem, que evita vazamentos de memória, deleções duplas e comportamento indefinido. Vamos analisá-la com exemplos reais para que você não apenas memorize a regra, mas entenda por que ela funciona.



Imagine: você escreveu uma classe que aloca memória no construtor. O compilador gentilmente gera para você o construtor de cópia e o operador de atribuição. Mas ele faz isso de forma rasa (shallow copy). E é aí que começam os problemas. A Regra dos Três diz: se sua classe define explicitamente um destrutor, construtor de cópia ou operador de atribuição, provavelmente você precisa definir todos os três. Por quê? Porque isso é um sinal de que sua classe gerencia algum recurso (memória, arquivo, conexão de rede) e a cópia deve ser profunda.



Problema: cópia rasa (shallow copy)

Vejamos um exemplo típico. A classe String, que armazena uma string C em memória dinâmica:



class String {private:    char* data;    size_t size;public:    // Construtor    String(const char* str) {        size = strlen(str);        data = new char[size + 1];        strcpy(data, str);    }

// Destrutor ~String() { delete[] data; }

// Construtor de cópia — gerado pelo compilador (raso) // Operador de atribuição — gerado pelo compilador (raso)

void print() const { std::cout << data << std::endl; }};


Tudo parece bem, até tentarmos copiar o objeto:



int main() {    String s1("Hello");    String s2 = s1;  // Chamada do construtor de cópia (gerado)    s1.print();    s2.print();    // O programa falhará com erro de double free!    return 0;}


O que aconteceu? O compilador gerou um construtor de cópia que simplesmente copiou o ponteiro data. Agora s1.data e s2.data apontam para o mesmo bloco de memória. Quando os objetos saem de escopo, seus destrutores são chamados. O primeiro destrutor libera a memória, o segundo tenta liberá-la novamente — double free. O programa trava ou se comporta de forma imprevisível.



Solução: Regra dos Três em ação

Para corrigir a situação, é necessário implementar todas as três funções-membro especiais. Isso é a Regra dos Três. Vamos escrever a versão correta da classe:



class String {private:    char* data;    size_t size;public:    // Construtor    String(const char* str) {        size = strlen(str);        data = new char[size + 1];        strcpy(data, str);    }

// Destrutor ~String() { delete[] data; }

// Construtor de cópia — cópia profunda String(const String& other) { size = other.size; data = new char[size + 1]; strcpy(data, other.data); }

// Operador de atribuição — cópia profunda String& operator=(const String& other) { if (this == &other) return *this; // proteção contra autoatribuição

delete[] data; // libera recursos antigos

size = other.size; data = new char[size + 1]; strcpy(data, other.data);

return *this; }

void print() const { std::cout << data << std::endl; }};


Agora a cópia funciona corretamente: cada objeto possui seu próprio bloco de memória. A atribuição também está

Blogs

Book Recommendations