Boost Weak Pointer (weak_ptr)

Para ver o artigo anterior da série: Boost Shared Pointer

No ultimo post vimos como funciona o shared_ptr da Boost e o problema da referência circular. Pensando em como resolver esse problema foi então criado o weak_ptr (ponteiro fraco), que também é um smart pointer, mas possui algumas restrições de uso:

  • Um weak_ptr só pode ser criado a partir de um shared_pointer
  • O ponteiro armazenado pelo weak_ptr somente pode ser usado após a chamada do método lock.

Voltando ao exemplo anterior, vamos modificar a classe um pouco:

#include <boost/shared_ptr.hpp>
#include <boost/weak_ptr.hpp>
#include <string>

class Person;

class Person
{
    public:
        typedef boost::shared_ptr<Person> PersonPtr_t;
        typedef boost::weak_ptr<Person> PersonWeakPtr_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;
        PersonWeakPtr_t m_Pai;
};

Agora a classe Person armazena um weak_ptr para o pai ao invés de um shared_ptr. A decisão sobre quem vai ser weak e quem vai ser shared depende do projeto em si. No exemplo acima, tanto faz, escolhi o pai pensando que o certo seria o pai armazenar uma lista de filhos (que não implementei para deixar o exemplo simples) e a estrutura acabaria ficando parecido com uma arvore, e geralmente em arvores o que importa mesmo é a lista de filhos de cada nó, sendo a referencia para o pai apenas uma conveniência.

Mas voltando a weak_ptr, note que o método SetPai recebe como parâmetro um shared_ptr, não um weak_ptr. Implementamos assim pois um weak_ptr deve ser sempre inicializado a partir de um shared_ptr ou um outro weak_ptr. Se for o caso, pode ser adicionado uma sobrecarga com weak_ptr também.

Utilizando a nova classe:

int main(int argc, char **argv)
{
    Person::PersonPtr_t pai(new Person());
    pai->SetName("Pai");

    Person::PersonPtr_t filho(new Person());
    filho->SetName("Filho");
    filho->SetPai(pai);

    pai->SetFilho(filho);

    return 0;
}

Sim! Não mudou nada em relação ao exemplo do post anterior. Para deixar mais interessante, vamos criar um novo método e usá-lo:

#include <boost/shared_ptr.hpp>
#include <boost/weak_ptr.hpp>
#include <string>
#include <iostream>

class Person;

class Person
{
    public:
        typedef boost::shared_ptr<Person> PersonPtr_t;
        typedef boost::weak_ptr<Person> PersonWeakPtr_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;
        }

        inline const std::string &GetName() const
        {
            return(m_Name);
        }

        inline const std::string *GetNomePai()
        {
            PersonPtr_t ptr = m_Pai.lock();

            return(ptr ? &ptr->GetName() : NULL); 

        }

    private:
        std::string m_Name;

        PersonPtr_t m_Filho;
        PersonWeakPtr_t m_Pai;
};

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

    Person::PersonPtr_t pai(new Person());
    pai->SetName("Pai");

    Person::PersonPtr_t filho(new Person());
    filho->SetName("Filho");
    filho->SetPai(pai);

    pai->SetFilho(filho);

    const std::string *name = filho->GetNomePai();
    cout << "Nome do pai: " << (name ? *name : "sou orfao") << endl;

    return 0;
}

Pronto, agora já sabemos como usar o weak_ptr. Note que no método GetNomePai verificamos se o ponteiro ptr é valido antes de usá-lo. Isto deve ser feito pois um weak_ptr não garante que um objeto vai ser valido, apenas garante que uma referencia é valida ou null.

Isso quer dizer que se fizermos:

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

    Person::PersonPtr_t pai(new Person());
    pai->SetName("Pai");

    Person::PersonPtr_t filho(new Person());
    filho->SetName("Filho");
    filho->SetPai(pai);

    pai->SetFilho(filho);
    pai.reset(); //apagando a referencia

    const std::string *name = filho->GetNomePai();
    cout << "Nome do pai: " << (name ? *name : "sou orfao") << endl;

    return 0;
}

Neste novo exemplo, ao invés do nome do pai, vai ser impresso a mensagem “sou órfão”, porque quando o reset do ponteiro do pai é invocado, o objeto pai é destruído. Nesse caso, o weak_ptr nos retorna null. Se ao invés do weak_ptr estivéssemos usando um ponteiro comum, acessaríamos um ponteiro invalido.

No próximo post, vamos ver como usar smart pointers com vetores (arrays).

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

Uma resposta para “Boost Weak Pointer (weak_ptr)”

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

    [...] Shared Pointer, Weak Pointer e Scoped [...]

Deixe uma resposta