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/catchpara liberar recursos manualmente. - A biblioteca padrão já oferece wrappers RAII como
std::unique_ptrestd::lock_guard. - Copiar um objeto RAII geralmente é indesejado; use
= deleteou implemente semântica de movimento.
#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;
}