quinta-feira, 8 de novembro de 2007

Dica C++: Usando auto_ptr

A auto_ptr é uma classe parte da STL que permite guardar a referência para um objeto de qualquer tipo. Quando se cria um objeto da classe auto_ptr, passando para ela o objeto apontado, ele terá a posse do objeto apontado.

Por exemplo:

class ClasseQualquer
{
public:
int valor;
};

auto_ptr< ClasseQualquer > meuAutoPtr(
  new ClasseQualquer );


O objeto criado pela instrução new ClasseQualquer será agora o objeto apontado por meuAutoPtr.


Quando a instância de meuAutoPtr for destruída, ela destrói o objeto que ela aponta. Assim, quando o escopo da variável meuAutoPtr terminar, ela automaticamente irá liberar da memória o (dar um delete no) objeto para o qual ela aponta.


A classe auto_ptr possui o método get para retornar o objeto apontado:


meuAutoPtr.get(); // irá retornar a instância de ClasseQualquer


Logo, para alterar o atributo valor:


meuAutoPtr.get()->valor = 100;


Só que auto_ptr também sobrecarrega o operador ->, fazendo com que ele também retorne o objeto apontado (sendo X uma classe qualquer a assinatura do método seria "X* operator ->() const;". Devo falar mais sobre sobrecarga de operadores em postagens futuras.). Logo, podemos usar


meuAutoPtr->valor = 100;


como se meuAutoPtr fosse o próprio objeto de ClasseQualquer.


auto_ptr possui o método release, para liberar a memória do objeto apontado por ela:


meuAutoPtr.release(); // destroi a instância de ClasseQualquer


E o método reset, para fazer com que ela aponte para outro objeto. O método reset irá destruir o objeto apontado antes de receber o novo:


meuAutoPtr.reset( new ClasseQualquer );


Na grande maioria das vezes, não precisamos usar os métodos oferecidos por auto_ptr pois sua utilidade maior é mesmo a de poder guardar a referência para um objeto e destruí-lo automaticamente assim que seu escopo terminar.

void FuncaoExemplo()
{
// Um objeto de ClasseQualquer é
// instanciado

auto_ptr< ClasseQualquer > meuAutoPtr(
new ClasseQualquer );
 
// Podemos acessar o objeto criado atraves
// do objeto auto_ptr de forma praticamente
// transparente (devido ao operator ->)

meuAutoPtr->valor = 100;
 
cout << "O valor é: " << meuAutoPtr->valor;
 
// O MAIS IMPORTANTE:
// Nao e' preciso desalocar a memoria que
// foi criada e passada para o auto_ptr.
// Como o escopo da variavel meuAutoPtr
// terminou ela deleta o objeto apontado

}

Essa é uma maneira simples de criar objetos com new, sem que seja preciso usar delete (ou seja, sem que seja preciso se preocupar com a memória alocada).


Cuidados no uso de auto_ptr

Quando criamos um objeto da classe auto_ptr, ele geralmente não deve ser um ponteiro. Isso porque, sendo ponteiro, nos obrigaria a usar new e delete, o que faria perder o sentido da classe.
void FuncaoExemplo2_UsoSemSentido()
{
auto_ptr< ClasseQualquer > *meuAutoPtr =
new auto_ptr< ClasseQualquer >(
new ClasseQualquer );
 
( *meuAutoPtr )->valor = 100;
 
cout << "O valor é: " <<
( *meuAutoPtr )->valor;
 
delete meuAutoPtr;
}

Veja que foi preciso acessar o valor do ponteiro meuAutoPtr, usando asterisco...

( *meuAutoPtr )->valor = 100;

...para não precisar acessar usando get:

meuAutoPtr->get()->valor = 100;

Outro cuidado importante que deve-se ter é o atribuir uma variável da classe auto_ptr a outra. A atribuição passa a posse do objeto para a variável receptora (do lado esquerdo). Isto porque auto_ptr admite somente um dono para um objeto:

// Cria umDono possuindo uma instancia de
// ClasseQualquer

auto_ptr< ClasseQualquer > umDono(
new ClasseQualquer );
 
// Faz com que novoDono seja o novo dono da
// instancia e com que umDono não seja mais
// dono de nada !!!

auto_ptr< ClasseQualquer > novoDono = umDono;
 
umDono->valor = 100; // ERRO !
 
novoDono->valor = 100; // OK

Conclusão


Podemos usar a classe auto_ptr para nos ajudar a criar instâncias de outras classes sem que precisemos nos preocupar em gerenciar a memória alocada.


Em tempo, a classe pertence à biblioteca memory e é declarada dentro do namespace std.

Nenhum comentário: