Aquisição de Recurso é Inicialização (conhecido pelo acrônimo RAII para o termo em língua inglesa Resource Acquisition Is Initialization) é um padrão de projeto de software para C++, D e Rust que combina a aquisição e liberação de recursos com inicialização e destruição de objetos.

Segundo a RAII, o uso de memória de um objeto inicia-se na sua declaração, e termina quando o objeto sai do escopo, seja porque a execução chegou ao final do bloco, ou porque uma exceção foi lançada. A liberação de memória é possível através do uso dos métodos destrutores da classe – a liberação da memória no destrutor garante que ela será realizada em qualquer caminho que o fluxo seguir, tornando o código seguro.

Exemplo

Alocação dinâmica de memória Usualmente em C++, quando se é necessário alocar um array e outros objetos de forma dinâmica, isto é, durante a execução do programa, o operador new é utilizado para realizar a alocação de memória (que no contexto de RAII é um recurso), enquanto o operador delete[] é utilizada para a desalocação, como no exemplo abaixo:void exemplo() { // Realiza a alocação dinâmica de memória int* array = new int;

// Utiliza a variável array = 25;

// Realiza a desalocação de memória quando não mais necessária delete[] array; }Caso a desalocação com o operador delete[] não seja realizada após o uso da variável array ter sido encerrado, a memória alocada inicialmente com o operador new permanecerá alocada até o término da execução do programa, pois C++ não contém um coletor de lixo () por padrão; este fenômeno é conhecido como vazamento de memória (). Múltiplas ocorrências de vazamento de memória, sobretudo em estruturas de repetição (tais como for e while), podem eventualmente esgotar a memória do sistema conforme a aplicação é executada, levando a diminuição do desempenho e a falhas. O fenômeno é demonstrado no exemplo abaixo:void exemplo() { // Realiza a alocação dinâmica de memória int* array = new int;

// Utiliza a variável array = 25;

} /* A inclusão do operador de desalocação foi acidentalmente esquecida, a execução da função terminará mas a memória não utilizada permanecerá alocada para a variável array. */Para solucionar este problema através do RAII, é possível encapsular e abstrair um array alocado dinamicamente através de uma classe RaiiArray cujo construtor RaiiArray(std::size_t tamanho) e destrutor ~RaiiArray() realizem a alocação e desalocação da memória necessária, respectivamente. Similar ao comportamento de um ponteiro inteligente, a alocação é realizada quando a classe é instanciada através do construtor, e a desalocação ocorre automaticamente pelo destrutor quando a execução do programa atinge o fim do escopo onde o objeto se encontra, como demonstrado no exemplo abaixo. O operador [] foi incluído para que o acesso aos índices do array seja feito de maneira similar a forma convencional.class RaiiArray { private: int* array;

public: // O construtor realiza a alocação dinâmica de memória RaiiArray(std::size_t tamanho)

this->array = new int[tamanho];

// O destrutor realiza a desalocação de memória ~RaiiArray()

delete[] this->array;

// O operador [] retorna uma referência para o índice solicitado int & operator [] (std::size_t i)

return this->array[i];

void exemplo() { // Instancia a classe e realiza a alocação dinâmica de memória RaiiArray array = RaiiArray(10);

// Utiliza a variável array = 25;

} /* O destrutor da instância é automaticamente executado quando ela sai do escopo da função, desalocando a memória alocada internamente pela instância e inibindo a ocorrência de vazamento de memória. */Através do uso de templates, a classe acima poderia ser generalizada para um tipo genérico qualquer, como demonstrado abaixo:#include <string>

template<typename T> class RaiiArray { private: T* array;

public: RaiiArray(std::size_t tamanho)

this->array = new T[tamanho];

~RaiiArray()

delete[] this->array;

T & operator [] (std::size_t i)

return this->array[i];

void exemplo() { RaiiArray<int> int_array = RaiiArray<int>(10); RaiiArray<float> float_array = RaiiArray<float>(10); RaiiArray<std::string> string_array = RaiiArray<std::string>(10); RaiiArray<const char*> const_char_ptr_array = RaiiArray<const char*>(10);

int_array = 25; float_array = 25.0f; string_array = std::string("25"); const_char_ptr_array = "25"; }É importante citar que a biblioteca padrão do C++ moderno fornece alternativas RAII melhores para alocação dinâmica de memória, como std::vector, std::unique_ptr, std::shared_ptr e std::weak_ptr.

Arquivo A classe abaixo encapsula chamadas do gerenciamento de arquivos da biblioteca padrão do C usando RAII:

#include <cstdio>

class arquivo { std::FILE* m_ptrArquivo;

public: arquivo(const char* nomeArquivo) : m_ptrArquivo(std::fopen(nomeArquivo, "w+"))

if (!m_ptrArquivo) throw std::runtime_error("Erro ao abrir o arquivo.");

// atribuição e cópia não implementados, prevenindo o uso arquivo(const arquivo&) = delete; arquivo& operator=(const arquivo&) = delete;

~arquivo() { if (std::fclose(m_ptrArquivo) != 0) { // lida-se com erros do sistema de arquivos, fclose() pode falhar

void escreve(const char* texto) { if (std::fputs(texto, m_ptrArquivo) == EOF) throw std::runtime_error("Erro ao escrever no arquivo.");

Pode-se usar a classe acima da seguinte forma:

void exemplo_de_uso() { // abre o arquivo (adquire e inicializa o recurso) arquivo arquivoLog("arquivoLog.txt");

arquivo.escreve("Olá, log!");

// continua usando arquivoLog...

// lança exceções ou retorna sem se preocupar em fechar o recurso, // ele é fechado automaticamente quando seu escopo termina }

A essência do idioma RAII é que a classe arquivo encapsula o gerenciamento do recurso FILE*, adquirindo e liberando automaticamente a memória, sem que seja necessário se preocupar com isso.

Uso e alternativas em outras linguagens

Objetos Java são destruídos automaticamente em tempos indeterminados pelo coletor de lixo. Os recursos então devem ser manualmente fechados pelo programador. O exemplo anterior deve ser escrito da seguinte maneira em Java:

void java_example() { // abre o arquivo (adquire o recurso) LogFile logfile = new LogFile("logfile.txt") ;

try { logfile.write("hello logfile!") ;

// continua usado logfile ...

} finally { // fechamento explícito do recurso logfile.close();

A responsabilidade de liberar o recurso recai sobre o programador em cada ponto onde o recurso é usado, e não de forma genérica em um local único.

Categoria:Programação orientada a objetos Categoria:C++