Auto Pointer (auto_ptr)

Para ver o artigo anterior da série: Smart Pointer – Introdução

Agora que já vimos o funcionamento básico de um smart pointer, vamos ver como usar um smart pointer que vem junto com a biblioteca padrão do C++.

Um ponteiro automático (auto_ptr) é semelhante a classe de smart pointer que mostrei no post anterior. Ele mantem o ponteiro para o dado em uma variável interna e quando seu destrutor é chamado ela se encarrega de destruir (fazer um delete) no ponteiro.

Mas a nossa classe de smart pointer tem um serio problema, se fizermos:


void funcao()
{
	Pointer p = new int(0);
	Pointer p2 = p;
}

//ou entao

Pointer alocaInt(int v)
{
	return(Pointer(new int(v));
}

O código acima simplesmente vai dar problemas, porque o int alocado com new vai ser “deletado” duas vezes,a primeira por p e a segunda por p2! Ops! Para evitar isso, podemos solucionar o problema usando algumas técnicas como:

  • Declarar o construtor de cópia e operador de atribuição como privados, bloqueando assim cópias de ponteiros (não resolve o problema, mas evita que ele ocorra).
  • Gerenciar os ponteiros para que apenas uma instância do objeto Pointer apague ele (usando contagem de referencia por exemplo).
  • Simplesmente quando um pointer for inicializado usando construtor de cópia ou operador de atribuição, ao invés de copiarmos os valores, vamos move-los (solução do auto_ptr).

Todas as soluções acima possuem seus prós e contras, não vou discutir aqui qual é melhor ou pior, porque cada uma acaba sendo melhor que a outra em determinados problemas. A solução do auto_pointer (que consiste em mudar o “dono” do ponteiro) é melhor (por este aspecto apenas) que a contagem de referencia por não precisar alocar um objeto extra para contar referencias ou criar um membro na classe para tal fim. Mas em contra-partida, não permite tanta flexibilidade quanto esta (não podemos usar o auto_ptr com conteiners da STL, por exemplo).

Se você simplesmente alocar um objeto na heap, fazer algumas operações e depois destruí-lo, o auto_ptr é uma das melhores soluções, pois uma boa implementação não traz overhead algum. Exemplo:


#include <memory>
#include <iostream>

int main(int argc, char **argv)
{
    using namespace std;

    auto_ptr<int> p(new int(3));

    cout << p.get() << endl;

    auto_ptr<int> p2 = p;

    cout << p.get() << endl;
    cout << *p2 << endl;

    return(0);
}

No incrível exemplo acima, primeiro criamos um auto_ptr p e alocamos um inteiro. Após imprimir o valor do ponteiro, criamos um novo auto_ptr p2 e inicializamos ele com p. Nesse momento, p2 passa a apontar para o inteiro alocado anteriormente (agora ele é o dono) e p simplesmente aponta para NULL.

No final do código imprimimos o valor do ponteiro p e o valor apontado por p2 (o inteiro 3).

Outro exemplo:


#include <memory>
#include <iostream>

using namespace std;

class Objeto
{
      public:
             Objeto(int v): m_valor(v){}

             ~Objeto()
             {
                          cout << "obj " << m_valor << " morreu" << endl;
             }

             int valor(){ return m_valor; }
      private:
              int m_valor;

};

auto_ptr<Objeto> alocaObj(int v)
{
    return(auto_ptr<Objeto>(new Objeto(v)));
}

int main(int argc, char **argv)
{
    using namespace std;

    try
    {
        auto_ptr<Objeto> p = alocaObj(5);

        cout << p->valor() << endl;

        alocaObj(10);

        throw "danou-se";
    }
    catch(...)
    {
        cout << "ops!!!" << endl;
    }

    return(0);
}

Esse exemplo é muito mais bacana que o anterior! Tem até exceções! O legal desse exemplo é que podemos ver que não é preciso se preocupar em destruir qualquer um dos objetos alocados, porque o auto_ptr se encarrega disso. Note também que caso alguem lance um exceção, o auto_ptr não nos deixa na mão e se encarrega de limpar a sujeira.

Outro detalhe também é que na segunda chamada a alocaObj nós nem mesmo nos preocupamos em pegar o valor de retorno, e o auto_ptr mesmo assim deu conta do recado!

Conclusão

Para casos simples onde precisamos apenas controlar a vida de um objeto no escopo de uma função ou entre chamadas de funções, ao invés de usarmos delete manualmente (sujeito a erros, pois em algum fluxo podemos esquecer de usa-lo) o auto_ptr vem a calhar como uma ferramenta simples e eficiente. No próximo post vamos começar a ver as classes de smart_pointer da boost, que podemos utilizar inclusive com a STL!

Próximo artigo da série: Boost Shared Pointer

Referências

Using auto_ptr Effectively

Wikipedia

Uma resposta para “Auto Pointer (auto_ptr)”

  1. Caloni.com.br » Blog Archive » Últimas pesquisas na blogosfera nacional Disse:

    [...] Auto Pointer (auto_ptr) [...]

Deixe uma resposta