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