smart pointer: auto_ptr

Bom, voltando a falar de C++…. Desculpa a demora, mas infelizmente o meu querido amigo V.N. não tinha devolvido meu caderno de rascunhos que eu fiz alguns rascunhos interessantes para postar aqui.

Mas, deixando as desculpas para lá, vamos ao que interessa. . .

Delete? free? Nunca mais!!!

Bom, primeiramente, a pergunta que não quer calar: “O que é um auto_ptr?”. Para quem se lembra de posts passados, simplesmente, um auto_ptr é um smart_pointer, ou seja, um objeto que implementa o padrão de projeto proxy.

Mas precisamente, sua função é a de resolver os “delete” de c++, deixando a função de desalocar um recurso para essa classe, quando o escopo em que o auto_ptr for terminado. Ou seja, o auto_ptr deleta para você a memoria que você alocou no final do escopo em que ele foi declarado(por isso que ele pode ser considerado um smart_pointer).

O auto_ptr não é nada mais além de uma template de uma classe, que tem como atributo interno um ponteiro para um tipo(já que ela é uma template, esse tipo pode variar…) e também sobrecarrega os operadores de ponteiros( “*” , “->” , …) para que você possa lidar com o objeto de forma que ele pareça um ponteiro(por isso que ele é um proxy).

Vale a pena dizer que o auto_ptr é da biblioteca padrão de C++, e que esta no namespace std do cabeçalho <memory>.

Contrato/API

Bom, essa classe nada mais faz do que deletar a memoria alocada no final, então, consequentemente, ele não “deveria” não interferir no acesso a memoria que esta alocado ;).

Inicializando ponteiros(ou: Adquirindo recursos)

Então vamos a como você faria para inicializar um ponteiro para um tipo “MyType” sem o auto_ptr:

{	/* começa aqui um escopo imaginário . . . */
	MyType *my_type=NULL ;
	my_type=new MyType ;

Então, com auto_ptr nos teríamos:

{	/* começa aqui um escopo imaginário . . . */
	auto_ptr<MyType> my_type( new MyType );

Sacou a diferença? Menos código e código mais natural? Ainda não sacou? Então, vamos pensar: No primeiro código, você não ganha o presente, que é desalocar o recurso no final do escopo ;). E veja que o segundo, o código e mais limpo e natural, e se escreve em uma linha ;).

Destruindo Ponteiros(ou: Liberando Recursos)

Certo, beleza, mas é ai? Como eu desaloco um recurso no final da minha função ou no final do meu “escopo”? No primeiro exemplo, vamos ver como “desalocar”/”liberar” o primeiro exemplo que fizemos:

	delete my_type ;
}	/* Fim do escopo imaginário. . . */

Agora vamos ver com o auto_ptr:

	/* Há!! Pegadinha do malandro!!! Aqui você não faz nada . . . */
}	/* Fim do escopo imaginário. . . */

Acessando Ponteiros/Recursos

Isso, vamos ver o como acessar o recurso com o auto_ptr. Agora, primeiramente, vamos ver como acessar o ponteiro do primeiro exemplo, o sem auto_ptr:

	int x = my_type->x ;
	my_type->x = 12345 ;

Tudo bem, mas como acesso isso com auto_ptr? Através de métodos? Gets and Sets? Magia negra? Não, da mesma forma. Isso mesmo, da mesma forma.

Não fiquem esperando que eu recorte e cole o código acima, porque é o mesmo :p. Isso se deve porque o “contrato” do auto_ptr não fica na parte de acesso ao recurso, e sim na criação e destruição do recurso. Então, teoricamente, o auto_ptr não deve interferir nesse sentido.

Outros detalhes da API

Certo, mas como eu poderia liberar um recurso antes do escopa acabar tranquilamente? Usar um delete? Não, é uma variável, não se pode dar um delete nela ;). Então se faz necessário que tenha um método que desaloque o recurso antes de terminar o escopo. Para isso existe o método “reset()“, que você usa simplesmente assim:

	my_type.reset();

Caso você passe um argumento sendo uma endereço de memoria, esse novo endereço vai ser o endereço que o auto_ptr vai fazer proxy dele. Estilo:

	my_type.reset( new MyType );

Veja que foi usado o “.” no lugar “->“. Isso porque você esta acessando o método da classe auto_ptr, e não da classe que ele esta fazendo “proxy“.

Mas uma coisa para se ressaltar na API, seria o construtor de copia do auto_ptr, porque, quando você faz uma atribuição de um auto_ptr para outro, a que é atribuído não é atribuído nada, ele só passa a fazer proxy do recurso do outro auto_ptr e o outro, passa a fazer proxy de nada(ou NULL, em outras palavras…). Isso é importante de lembrar porque o auto_ptr passa a ser invalido, então não tente usar ele depois de atribuir para outro auto_ptr.

Mais uma coisa, existe também um método chamado “.get()” que retorna um objeto que é a copia do objeto que o auto_ptr esta fazendo proxy. Então eu poderia fazer assim:

	MyType aux = my_type.get();

E também existe um método “.release()“, que libera o endereço que o auto_ptr esta fazendo proxy para um ponteiro normal. Legal para quando você precisa saber do endereço de onde esta, e/ou para passar para fora de funções caso você não retorne via auto_ptr. Então seria assim:

	MyType *aux = my_type.get();

Sabendo de tudo isso, vamos a um exemplinho básico, só pra ver a utilidade disso.

On The Road!!!!

Agora vamos ao exemplos. Fiz um programa para testar o tempo de criação, destruição e acesso de recursos. Você podem ver o código aqui.

Vale a pena dizer que eu usei a minha classe de medir tempo, para mais detalhes, veja aqui.

Falado isso, só falta mostrar a saída dos resultado:

Tempo de criação e destruição:
Ponteiro normal: 60s
Ponteiro automático: 60s
auto_ptr:
Teste de escrita: 53s
ptr normal:
Teste de escrita: 53s
auto_ptr:
Teste de acesso: 53s
ptr normal:
Teste de acesso: 53s

Bom, podemos ver que os tempos foram iguais ;). Porque? Simples, a proposta do auto_ptr é justamente não interferir no acesso e criação, e sim só destruir o objeto quando o escopo termina ;).

Bom, da para ver que não existe motivos para não usar auto_ptr(a não ser por alguns recursos técnicos, como ele não se comportar bem com conteniers da biblioteca padrão . . .), e também usar não gera overhead nenhum. Então, porque não usar neh?

Para considerações finais, só tenho a dizer que usei o gcc-4.3 e o pc que eu rodei esse exemplo era bem rápido. Portanto, se forem repetir o exemplo, coloquem -O2 ou equivalente no gcc e diminua o numero de operações que são feitas.

Dicas para Usar um auto_ptr(Ou: O que não fazer com auto_ptr):

Advertisements

3 responses to “smart pointer: auto_ptr

  • diogo_dutra

    Este auto_ptr &eacute; um reference counter ou um desalocador de ponteiros ao final de escopos?
    <br />N&atilde;o entendi direito como o auto_prt trabalha…
    <br />Um dos conceitos de smart pointer &eacute; o de reference counter, onde ele vai contando as refer&ecirc;ncias para um objeto (ponteiros apontando para a mesma posi&ccedil;&atilde;o de mem&oacute;ria), qdo esse contador chega a zero a mem&oacute;ria &eacute; liberada.
    <br />O auto_ptr trabalha dessa forma? Ou o auto_ptr implementa outro tipo de conceito?
    <br />Pq pelo oq vc escreveu o auto_ptr somente d&aacute; um delete qdo o escopo termina. Ent&atilde;o de acordo com seu texto se eu dar um new dentro de uma fun&ccedil;&atilde;o ao final da mesma este ser&aacute; deletado, afinal &eacute; um escopo. Ent&atilde;o se eu der um new dentro de um construtor da minha classe qdo terminar a execu&ccedil;&atilde;o deste construtor ser&aacute; dado um delete! Aparentemente pra mim isso n&atilde;o faz sentido, e se realmente esse for o funcionamento do auto_ptr ent&atilde;o ele tem um uso MTO limitado.

  • psychomantys

    O smart pointer não necessariamente implementa um contador de referencia ;).
    O que faz uma classe ser um smart pointer seria o fato dessa classe implementar uma classe de ponteiros mais especificamente.
    Normalmente, muitos smart pointers realmente implementam contadores de referencia, mas eles não são obrigatórios ;).

    Sim, realmente o auto_ptr só faz isso, desaloca a memoria/recurso no final do escopo. O que você disse sobre o construtor também e verdade, mas veja que você esta pensando sobre criar um auto_ptr no escopo do construtor. Ai é que ta: Você tem que criar o auto_ptr no escopo da classe, para enquanto a classe existir, o auto_ptr também existir. Depois é que você aloca o recurso para o auto_ptr, no construtor.
    Vou explicar com exemplo:
    /* Como você falou: */
    class Foo{
        Foo(){
            auto_ptr<int> x( new int );
    } /* Realmente, aqui isso sai estranho… */
    };

    /* No escopo da classe: */
    class Foo{
    auto_ptr<int> x ;
    Foo() : x( new int ) {
    *x = LOST_MAGIC_NUMBER ;
    } /* Agora ele n&atilde;o some ate o destrutor */
    };

    Sacou? Ponteiro não é deletado/liberado a menos que seja explicitamente. O resto é deletado/liberado no final do escopo, sem que você ordene isso(ou seja, implicitamente).

    PS1:. Esses tópicos estão seguindo uma ordem. Tem um tópico do meu blog com smart pointer, proxy e agora auto_ptr. No próximo post vou colocar algumas coisas nesse sentido de smart pointer, mas uns mais complexos(possivelmente shared_ptr ou um vetor de ponteiros que não precisa desalocar no final… Sei lah, uma parada assim…)

    PS2:. Tem varias coisas que você não pode fazer com um auto_ptr. Acho que vou reeditar o post para colocar isso de forma rápida. Não ia falar disso, mas já que agora to vendo que existe alguem vendo o que eu escrevo, criei coragem ;).

  • psychomantys

    Entendi o que você queria dizer diogo ;).

    Vou modificar lá para fazer mais sentido.

    PS:. De vez em quando eu não entendo bem o que as pessoas falam… Chamam isso de altismo . . . :p

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: