Da trilha de C++ Este conceito ainda não saiu como card do dia. ir para o card de hoje

C++ Memória Intermediário

RAII

RAII (Resource Acquisition Is Initialization) é um idioma fundamental em C++: você adquire um recurso no construtor de um objeto e o libera no destrutor. Assim, o tempo de vida do recurso fica amarrado ao tempo de vida do objeto, garantindo liberação automática mesmo em caso de exceções.

O grande benefício é a segurança a exceções: se uma exceção for lançada após a aquisição, o destrutor do objeto será chamado durante o stack unwinding, liberando o recurso. Isso evita vazamentos de memória, arquivos abertos, mutexes travados, etc.

A biblioteca padrão usa RAII extensivamente: std::vector, std::string, std::unique_ptr, std::lock_guard e std::fstream são exemplos clássicos. Ao usá-los, você não precisa se preocupar com liberação manual.

Neste card, vamos implementar uma classe que gerencia um recurso de forma RAII e comparar com a abordagem manual propensa a erros.

Pontos-chave

  • Adquira o recurso no construtor e libere no destrutor.
  • O destrutor é chamado automaticamente ao sair do escopo, inclusive durante exceções.
  • RAII elimina a necessidade de try/catch para liberar recursos manualmente.
  • A biblioteca padrão já oferece wrappers RAII como std::unique_ptr e std::lock_guard.
  • Copiar um objeto RAII geralmente é indesejado; use = delete ou implemente semântica de movimento.
raii.cpp
#include <iostream>
#include <fstream>
#include <string>
#include <stdexcept>

// Classe RAII que gerencia um arquivo
class FileHandle {
    std::fstream file_;
public:
    // Construtor adquire o recurso
    FileHandle(const std::string& filename) : file_(filename, std::ios::out) {
        if (!file_.is_open()) {
            throw std::runtime_error("Nao foi possivel abrir o arquivo");
        }
        std::cout << "Arquivo aberto: " << filename << '\n';
    }

    // Destrutor libera o recurso
    ~FileHandle() {
        if (file_.is_open()) {
            file_.close();
            std::cout << "Arquivo fechado\n";
        }
    }

    // Desabilita cópia (evita dupla liberação)
    FileHandle(const FileHandle&) = delete;
    FileHandle& operator=(const FileHandle&) = delete;

    void escrever(const std::string& texto) {
        file_ << texto << '\n';
    }
};

void usar_arquivo() {
    FileHandle fh("exemplo.txt");
    fh.escrever("Linha 1");
    // Se uma exceção for lançada aqui, o destrutor de fh será chamado
    throw std::runtime_error("Erro simulado");
}

int main() {
    try {
        usar_arquivo();
    } catch (const std::exception& e) {
        std::cerr << "Excecao capturada: " << e.what() << '\n';
    }
    // O arquivo já foi fechado automaticamente
    return 0;
}

Exercícios

  1. 1
    Fácil

    Contador RAII

    Crie uma classe Contador que incrementa um contador global no construtor e o decrementa no destrutor. No main, crie um bloco e instancie um Contador; imprima o valor do contador dentro e fora do bloco para ver o efeito.

    Resolver
  2. 2
    Médio

    Gerenciador de buffer RAII

    Implemente uma classe Buffer que aloca um array de int no construtor e o libera no destrutor. A classe deve ter um método set(int index, int value) e get(int index). No main, crie um Buffer de tamanho 5, preencha com valores e imprima. Em seguida, force uma exceção para verificar que a memória é liberada.

    Resolver
  3. 3
    Desafio

    Mutex com RAII (lock_guard caseiro)

    Implemente uma classe MeuLockGuard que recebe um std::mutex& no construtor, chama lock(), e no destrutor chama unlock(). Desabilite a cópia. No main, crie um std::mutex e uma função que usa MeuLockGuard para proteger uma seção crítica. Simule uma exceção dentro da seção crítica e verifique que o mutex é liberado.

    Resolver

Continue estudando