Boost Shared Pointer (shared_ptr)

Transcrição

Boost Shared Pointer (shared_ptr)
Boost Shared Pointer (shared_ptr)
Escrito por Bruno Crivelari Sanches - Última atualização Sex, 25 de Fevereiro de 2011 18:56
Para ver o tutorial anterior da série: Auto Pointer (auto_ptr)
Finalmente
vamos pointer
ver um verdadeiro
pointer
. O boost shared
é um smartsmart
pointer
que gerencia a vida dos objetos utilizando
contagem de referencias.
A contagem de referencias nada mais é do que um contador associado a cada objeto. Toda
vez que um smart pointer passa a apontar para um objeto, ele incrementa esse contador,
quando o smart pointer é destruído ou passa a apontar para outro objeto, o contador é
decrementado. E por fim, o smart pointer toda vez que decrementar o contador deve verificar o
seu valor, se chegou a zero, então ninguém mais referencia aquele objeto, logo ele pode ser
destruído.
Vamos ao primeiro exemplo:
#include <boost/shared_ptr.hpp> int main(int argc, char **argv) {
using namespace
boost;
//contador de referencias agora é 1
shared_ptr<int> p(new int(5));
//alterando
o valor do inteiro
*p = 3;
//contador de referencias é 2, p e p2 apontam para o mesmo
objeto
shared_ptr<int> p2 = p;
return 0;
//p2 e p sao destruidos, contador de
referencias chega a zero e int tambem é destruido. }
Simples não? Agora vamos criar um pequeno objeto e usa-lo com o shared pointer:
#include <boost/shared_ptr.hpp> #include <string> class Person {
public:
inline
void SetName(const std::string &name)
{
m_Name = name;
}
private:
std::string m_Name; }; int main(int argc, char **argv) {
using namespace boost;
shared_ptr<person> p(new Person());
p->SetName("Bruno");
return 0; }
Note que não existem diferença nenhuma de uso em relação a um ponteiro normal (assim
como os outros smart pointer que vimos anteriormente). Agora vamos fazer uma pequena
mudança na classe pessoa:
#include <boost/shared_ptr.hpp>
#include <string> class Person {
public:
typedef boost::shared_ptr<person>
PersonPtr_t;
inline void SetName(const std::string &name)
{
m_Name =
name;
}
inline void SetFilho(PersonPtr_t ptr)
{
m_Filho = ptr;
}
inline void SetPai(PersonPtr_t ptr)
{
m_Pai = ptr;
}
private:
std::string m_Name;
PersonPtr_t m_Filho;
PersonPtr_t m_Pai; }; int main(int argc,
char **argv) {
Person::PersonPtr_t pai(new Person());
pai->SetName("Pai");
1/4
Boost Shared Pointer (shared_ptr)
Escrito por Bruno Crivelari Sanches - Última atualização Sex, 25 de Fevereiro de 2011 18:56
Person::PersonPtr_t filho(new Person());
return 0; }
filho->SetName("Filho");
pai->SetFilho(filho);
Agora criamos dois objetos dinamicamente e ainda associamos um com o outro (pai passou a
possuir uma referencia para Filho). No final da execução da função main, o primeiro a ser
destruído é o ponteiro filho (lembre-se, em C++ a destruição de variáveis locais é sempre feita
na ordem inversa de criação). O ponteiro filho vai decrementar o contador de referencias que
vai chegar a um, pois o objeto “Pai” possui também um ponteiro para Filho. Em seguida, o
ponteiro pai vai ser destruído, como seu contador de referencias chega a zero, ele destrói o
objeto “Pai”, em consequência o destrutor de “Pai” é executado, forçando a destruição das
suas variáveis membros, o que significa que o membro “m_Filho” é destruído, nesse momento
o contador de referencias do filho finalmente chega a zero e ele é então destruído!.
A sequência de destruição fica parecida com:
filho.~shared_ptr() DecrementaContador(); pai.~shared_ptr() DecrementaContador();
Destroy() Person::~Person() m_Filho::~shared_ptr() DecrementaContador() Destroy()
Person::~Person() //Destrutor do filho é executado
Note que ainda não usei o método SetPai, que vai ser usado no próximo exemplo.
Referências Circulares
No exemplo anterior não utilizei o método SetPai porque ele causa um problema fatal (que vai
nos gerar um memory leak):
int main(int argc, char **argv) {
Person::PersonPtr_t pai(new Person());
pai->SetName("Pai");
Person::PersonPtr_t filho(new Person());
filho->SetName("Filho");
pai->SetFilho(filho); //contador de filho chega a 2
filho->SetPai(pai);
//contador de
pai chega a 2
return 0; }
No final do bloco, quando o ponteiro filho é destruído, ele decrementa o contador de
referências do filho, que chega a um, nesse caso, o objeto filho não é destruído (lembre-se, o
objeto pai também possui uma referência). Em seguida, o ponteiro pai é destruído, ele vai
então decrementar o contador de referencia do pai, que também chega a um, logo ele também
não é destruído!!!
Resultado: nosso programa não possuí mais nenhuma referência para os objetos, mas como
um referência o outro, a contagem de referencia nunca chega a zero, logo eles nunca são
destruídos!! É como aquele casal de namorados chatos no telefone:
2/4
Boost Shared Pointer (shared_ptr)
Escrito por Bruno Crivelari Sanches - Última atualização Sex, 25 de Fevereiro de 2011 18:56
Ela: Desliga amor
Ele: Não desliga você mor!
Ela: Só desligo depois de você amor!
Ele: Eu também só vou desligar depois de você mor!
…
Overhead
O shared pointer possui um pequeno overhead toda vez que ele é copiado (pois tem que ficar
administrando o contador de referencias). Mas o maior overhead, que pode pesar para
algumas aplicações (como jogos em consoles) é a própria criação do contador de referencias.
Toda vez que criamos um novo objeto e associamos ele a um shared_ptr, um objeto para
controle de referencias é criado (que é bem pequeno, deve ser do tamanho de um ou dois
ponteiros). Mas temos o custo da alocação de memória extra e da possível fragmentação (não
sei se a boost internamente usa algum pool para minimizar isso). Mas para a maioria das
aplicações, isso é irrelevante.
Na pagina da boost existe uma parte da documentação que mostra o overhead: Smart Pointer
Timings
.
! Conclusões
Os smart_pointers da boost são excelentes e resolvem muitos dos problemas que enfrentamos
no dia dia, eu recomendo muito o seu uso, é muito melhor do que fazer o seu próprio, pois o
shared_ptr resolve vários problemas que não tratamos aqui:
- Exception safe
- Multithread: é possível que varias threads compartilhem o mesmo objeto, não o mesmo
shared_ptr. E isso não torna o seu objeto thread safe, esse problema é do programador!
- Casts (que vamos ver no futuro)
- Já foi testado, testado, usado, consertado, …, etc
3/4
Boost Shared Pointer (shared_ptr)
Escrito por Bruno Crivelari Sanches - Última atualização Sex, 25 de Fevereiro de 2011 18:56
No próximo tutorial vamos explorar o problema da referencia circular e conhecer mais um smart
pointer da boost.
Próximo tutorial da série: Boost Weak Pointer
4/4

Documentos relacionados