SyntaxHighlighter

sexta-feira, 14 de outubro de 2011

Pequenas classes utilitarias utilizando RAII

Como post inicial deste Blog, vou falar um pouco de RAII, uma das grandes sacadas do C++, e que é mais uma das inúmeras features que surgiram inesperadamente na linguagem.

Todos os dias no deparamos com a necessidade de executar uma função ou trecho de código ao final de uma operação. São coisas como fechar um arquivo, liberar memória, resetar alguma coisa ao seu valor inicial, etc. Em geral, escrevemos algo parecido com:

void executeTask() {
    initResource();
    useResource();
    releaseResource();
}

Até aqui, tudo bem. Mas e no caso de múltiplos pontos de retorno no meio da função?

void executeTask() {
    initResource();
    if (resourceBusy()) {
        releaseResource();
        return;
    }
    if (anotherCondition()) {
        releaseResource();
        return;
    }
    useResource();
    releaseResource();
}

Ter de lembrar de liberar o recurso em cada possível ponto de retorno deixa o código muito feio. Sem contar que é muito fácil de esquecer algum ponto. Há ainda o fato de que exceções podem ser lançadas durante o processo, e o recurso nunca será liberado.

É aí que entra o RAII (Resource Acquisition Is Initialization). A ideia é se aproveitar do fato de o construtor e o destrutor de um objeto serem chamados automaticamente no início e no final de vida dele, e que esse tempo de vida é atrelado ao escopo onde o objeto foi declarado. Seque um exemplo:

struct ScopedResource {
    ScopedResource() { initResource(); }
    ~ScopedResource() { releaseResource(); }
};

void executeTask() {
    ScopedResource res;

    if (resourceBusy()) {
        return;
    }
    if (anotherCondition()) {
        return;
    }
    useResource();
}

O construtor de ScopedResource faz a inicialização, e o destrutor libera o recusro. Como o objeto do tipo ScopedResource foi declarado como uma variável simples (não ponteiro), ele vai ser destruído automaticamente quando a função terminar, não importando se foi por um return, pela função ter acabado ou por alguma exceção.

É muito interessante criar pequenas classes como estas, pois elas sempre podem ser o começo de uma classe wrapper ao redor do recurso que se liberar automaticamente. Desta forma teremos um objeto que, enquanto estiver estanciado, representa um recurso e possui formas de operar sobre ele, e que quando não for mais acessível o recurso é liberado.

Estes pequenos objetos auxiliares são muito úteis, não só para recuros reais, como arquivos, conexões e memória, mas também para quando precisamos restaurar o estado do sistema depois de alguma operação. É muito comum ter de se definir o ponteiro do mouse para a ampulheta durante uma operação demorada, e depois ter de restaurar o ponteiro original. Ou mudar a cor de pintura de um componente e depois ter de voltar à cor original. É só pensar no estado da aplicação como um recurso também, que depois de usado precisa ser liberado (restaurado ao seu valor original).