Pular para conteúdo

RAII e Gerenciamento de Recursos

RAII vincula a vida de um recurso a um objeto. A construção estabelece posse e a destruição libera. Aplica-se a memória, arquivos, sockets, locks e transações.

Limpeza determinística

Objetos automáticos são destruídos ao sair do escopo, inclusive durante o desempilhamento por exceção. Membros e bases liberam recursos em ordem inversa.

std::string first_line(std::filesystem::path const& path) {
    std::ifstream input{path};
    if (!input) throw std::runtime_error{"cannot open input file"};

    std::string line;
    if (!std::getline(input, line)) {
        throw std::runtime_error{"cannot read first line"};
    }
    return line;
} // input closes here on every exit path

Não é necessário um caminho explícito de close(). Destrutores usados para limpeza não devem deixar exceções escapar durante o desempilhamento.

Ferramentas de posse

  • valores diretos e contêineres: padrão preferível;
  • std::unique_ptr<T>: posse dinâmica exclusiva;
  • std::shared_ptr<T>: posse compartilhada por referência;
  • std::weak_ptr<T>: observação sem posse e quebra de ciclos;
  • ponteiros/referências crus: acesso sem posse com vida clara.

Prefira std::make_unique e std::make_shared quando adequados. Posse compartilhada tem custo atômico e não torna o objeto thread-safe.

Locks são recursos

std::mutex mutex;
std::vector<int> values;

void append(int value) {
    std::lock_guard guard{mutex};
    values.push_back(value);
}

O guard libera em toda saída. std::scoped_lock adquire vários mutexes com mecanismo de prevenção de deadlock, mas o protocolo maior ainda exige projeto.

Handle personalizado

Um wrapper deve adquirir ou receber um recurso, expor operações válidas, liberar no destrutor e definir cópia/movimentação. Frequentemente é movível, não copiável.

RAII versus coleta de lixo

GC recupera memória inalcançável conforme política do runtime. RAII encerra posse em limites determinísticos. C++ pode usar collectors e Java pode usar wrappers de escopo; os conceitos não são excludentes.

Exercícios

  1. Encapsule um handle de arquivo C em classe RAII movível.
  2. Explique vazamento por ciclo de shared_ptr sem aresta fraca.
  3. Explique por que lock() seguido de unlock() não é seguro sob exceções.