Menu
Coddy logo textTech

Regra dos Três / Cinco / Zero

Parte da seção Programação Orientada a Objetos do Journey de C++ da Coddy. Lição 25 de 104.

Quando sua classe gerencia recursos como memória dinâmica, você aprendeu que precisa de um destrutor personalizado, de um construtor de cópia e de um construtor de movimentação. Mas há um princípio orientador que ajuda você a decidir quais funções-membro especiais implementar: a Regra de Três, Cinco e Zero.

A Regra dos Três afirma: se você definir qualquer um destes três, deverá definir os três:

  • Destrutor
  • Construtor de cópia
  • Operador de atribuição por cópia

A Rule of Five amplia isso para o C++ moderno, adicionando operações de movimentação:

  • Destrutor
  • Construtor de cópia
  • Operador de atribuição por cópia
  • Construtor de movimentação
  • Operador de atribuição por movimentação
class Buffer {
    int* data;
    size_t size;
public:
    Buffer(size_t s) : size(s), data(new int[s]) {}
    
    ~Buffer() { delete[] data; }                          // 1. Destrutor
    Buffer(const Buffer& other);                          // 2. Construtor de cópia
    Buffer& operator=(const Buffer& other);               // 3. Atribuição de cópia
    Buffer(Buffer&& other) noexcept;                      // 4. Construtor de movimentação
    Buffer& operator=(Buffer&& other) noexcept;           // 5. Atribuição de movimentação
};

A Rule of Zero é a abordagem mais simples: se sua classe não gerencia recursos diretamente, não defina nenhuma dessas funções. Deixe o compilador gerá-las ou use ponteiros inteligentes e contêineres padrão que gerenciem os recursos para você.

class Player {
    std::string name;              // std::string gerencia sua própria memória
    std::vector<int> scores;       // std::vector lida com seus recursos
public:
    Player(std::string n) : name(n) {}
    // Nenhum destrutor, funções de cópia ou de move necessários!
};

Seguir essas regras evita bugs como exclusão dupla, vazamentos de memória e ponteiros pendentes que ocorrem quando algumas operações são definidas, mas outras estão ausentes.

challenge icon

Desafio

Fácil

Vamos criar uma classe TextBuffer que siga a Regra dos Cinco: implementando todas as cinco funções-membro especiais para gerenciar corretamente dados de caracteres alocados dinamicamente. Isso demonstrará como as operações de cópia e movimentação trabalham juntas para criar uma classe robusta que gerencia recursos.

Você criará dois arquivos para organizar seu código:

  • TextBuffer.h: Defina uma classe TextBuffer que armazene texto em um array de caracteres alocado dinamicamente. Sua classe precisa de:
    • Membros privados: um ponteiro char* chamado data para o conteúdo do texto e um size_t length para o tamanho da string (sem incluir o terminador nulo)
    • Um construtor parametrizado que receba uma string C (const char*), aloque memória, copie o conteúdo e imprima "TextBuffer created: <text>"
    • Um destrutor que libere a memória (se não for nula) e imprima "TextBuffer destroyed"
    • Um construtor de cópia que faça uma cópia profunda e imprima "TextBuffer copied"
    • Um operador de atribuição por cópia que trate a autoatribuição, libere os dados existentes, faça uma cópia profunda e imprima "TextBuffer copy-assigned". Retorne *this
    • Um construtor de movimentação (marcado como noexcept) que transfira a propriedade e imprima "TextBuffer moved". Deixe a origem em um estado vazio válido
    • Um operador de atribuição por movimentação (marcado como noexcept) que trate a autoatribuição, libere os dados existentes, transfira a propriedade e imprima "TextBuffer move-assigned". Retorne *this
    • Um método getText() que retorne o texto armazenado (retorne uma string vazia "" se os dados forem nulos)
    • Um método getLength() que retorne o tamanho
  • main.cpp: Demonstre todas as cinco funções-membro especiais em ação. Leia uma string de texto da entrada e, em seguida:
    • Crie um TextBuffer chamado original com o texto de entrada
    • Crie copied usando o construtor de cópia a partir de original
    • Crie another com o texto "Temporary"
    • Use a atribuição por cópia: another = original
    • Crie moved construindo-o por movimentação a partir de original usando std::move()
    • Crie target com o texto "Target"
    • Use a atribuição por movimentação: target = std::move(copied)
    • Imprima "--- Final State ---"
    • Imprima "original: <text> (length: <len>)" para cada buffer: original, copied, moved, another, target

Após as movimentações, os objetos de origem (original e copied) devem mostrar texto vazio com tamanho 0, enquanto os objetos de destino devem conter os dados transferidos. Isso demonstra a Regra dos Cinco em ação: todas as cinco funções trabalhando juntas para garantir um gerenciamento seguro de recursos.

Inclua <cstring> para funções de string como strlen e strcpy, e <utility> para std::move().

Experimente você mesmo

#include <iostream>
#include <string>
#include <utility>
#include "TextBuffer.h"

using namespace std;

int main() {
    string input;
    getline(cin, input);

    // TODO: Crie um TextBuffer chamado 'original' com o texto de entrada

    // TODO: Crie 'copied' usando o construtor de cópia a partir de 'original'

    // TODO: Crie 'another' com o texto "Temporary"

    // TODO: Use a atribuição por cópia: another = original

    // TODO: Crie 'moved' por construção de movimentação a partir de 'original' usando std::move()

    // TODO: Crie 'target' com o texto "Target"

    // TODO: Use a atribuição por movimentação: target = std::move(copied)

    // TODO: Imprima "--- Final State ---"

    // TODO: Imprima o estado de cada buffer neste formato:
    // "original: <text> (length: <len>)"
    // Imprima para: original, copied, moved, another, target

    return 0;
}
quiz iconTeste seus conhecimentos

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

Pratique por conta própria: Compilador de C++ online