Pergunta

Eu corri em toda enable_shared_from_this durante a leitura dos exemplos Boost.Asio e depois de ler a documentação que eu ainda estou perdido de como isso deve ser corretamente utilizado. Alguém por favor pode me dar um exemplo e / ou e explicação de quando usar esta classe faz sentido.

Foi útil?

Solução

Ele permite que você obtenha uma instância shared_ptr válida para this, quando tudo que você tem é this. Sem ele, você não teria nenhuma maneira de conseguir um shared_ptr para this, a menos que você já teve um como um membro. Este exemplo do impulso documentação para enable_shared_from_this :

class Y: public enable_shared_from_this<Y>
{
public:

    shared_ptr<Y> f()
    {
        return shared_from_this();
    }
}

int main()
{
    shared_ptr<Y> p(new Y);
    shared_ptr<Y> q = p->f();
    assert(p == q);
    assert(!(p < q || q < p)); // p and q must share ownership
}

O método f () retorna um shared_ptr válido, mesmo que ele não tinha nenhuma instância membro. Note que você não pode simplesmente fazer isso:

class Y: public enable_shared_from_this<Y>
{
public:

    shared_ptr<Y> f()
    {
        return shared_ptr<Y>(this);
    }
}

O ponteiro compartilhada que este retornou terá uma contagem de referência diferente do "próprio" um, e um deles vai acabar perdendo e segurando uma referência balançando quando o objeto é excluído.

enable_shared_from_this tornou-se parte do C 11 ++ padrão. Você também pode obtê-lo de lá, bem como de impulso.

Outras dicas

de artigo Dr Dobbs em ponteiros fracos, penso que este exemplo é mais fácil de entender (fonte: http: // drdobbs .com / CPP / 184402026 ):

... código como este não vai funcionar corretamente:

int *ip = new int;
shared_ptr<int> sp1(ip);
shared_ptr<int> sp2(ip);

Nenhum dos dois objetos shared_ptr sabe sobre o outro, de modo que ambos vão tentar liberar o recurso quando eles são destruídos. Isso geralmente leva a problemas.

Se uma função membro precisa de um objeto shared_ptr que possui o objeto que está sendo chamado, ele não pode simplesmente criar um objeto na mosca:

struct S
{
  shared_ptr<S> dangerous()
  {
     return shared_ptr<S>(this);   // don't do this!
  }
};

int main()
{
   shared_ptr<S> sp1(new S);
   shared_ptr<S> sp2 = sp1->dangerous();
   return 0;
}

Este código tem o mesmo problema que o exemplo anterior, embora de uma forma mais sutil. Quando ela é construída, o shared_ptr objeto sp1 possui o recurso recém-alocado. O código dentro do S::dangerous função membro não sabe sobre esse objeto shared_ptr, portanto, o objeto shared_ptr que ela retorna é distinta da sp1. Copiando o novo objeto shared_ptr para sp2 não ajuda; quando sp2 sai do escopo, ele irá liberar o recurso, e quando sp1 sai do escopo, ele irá liberar o recurso novamente.

A maneira de evitar esse problema é usar o enable_shared_from_this modelo de classe. O modelo tem um argumento tipo de modelo, que é o nome da classe que define o recurso gerenciado. Essa classe deve, por sua vez, ser derivado publicamente a partir do modelo; assim:

struct S : enable_shared_from_this<S>
{
  shared_ptr<S> not_dangerous()
  {
    return shared_from_this();
  }
};

int main()
{
   shared_ptr<S> sp1(new S);
   shared_ptr<S> sp2 = sp1->not_dangerous();
   return 0;
}

Quando você fizer isso, tenha em mente que o objeto no qual você chama shared_from_this deve ser propriedade de um objeto shared_ptr. Isso não vai funcionar:

int main()
{
   S *p = new S;
   shared_ptr<S> sp2 = p->not_dangerous();     // don't do this
}

Aqui está a minha explicação, de uma perspectiva de parafusos e porcas (resposta superior não 'clique' comigo). * Nota que este é o resultado de investigar a fonte para shared_ptr e enable_shared_from_this que vem com o Visual Studio 2012. Talvez outros compiladores implementar enable_shared_from_this diferente ... *

enable_shared_from_this<T> adiciona uma instância weak_ptr<T> privada para T que detém o ' uma verdadeira referência contagem ' para a instância do T.

Assim, quando você primeiro criar uma shared_ptr<T> para um novo T *, que T * 's weak_ptr interna é inicializado com um refcount de 1. Basicamente, a nova shared_ptr costas para esta weak_ptr.

T pode, então, em seus métodos, chamada shared_from_this para obter uma instância de shared_ptr<T> que backs para a mesma referência armazenado internamente count . Dessa forma, você sempre terá um lugar onde ref-count de T* é armazenado em vez de ter várias instâncias shared_ptr que não sabem sobre o outro, e cada um pensam que são o shared_ptr que está a cargo da T ref-contagem e excluí-lo quando seus ref-count chegar a zero.

Note que usando um boost :: intrusive_ptr não sofrem deste problema. Isso é muitas vezes uma maneira mais conveniente de contornar este problema.

É exatamente o mesmo em c ++ 11 e posterior:. É para permitir a capacidade de this retorno como um ponteiro compartilhado desde this dá-lhe um ponteiro bruto

Em outras palavras, ele permite que você transforme um código como este

class Node {
public:
    Node* getParent const() {
        if (m_parent) {
            return m_parent;
        } else {
            return this;
        }
    }

private:

    Node * m_parent = nullptr;
};           

a este:

class Node : std::enable_shared_from_this<Node> {
public:
    std::shared_ptr<Node> getParent const() {
        std::shared_ptr<Node> parent = m_parent.lock();
        if (parent) {
            return parent;
        } else {
            return shared_from_this();
        }
    }

private:

    std::weak_ptr<Node> m_parent;
};           

Outra forma é adicionar um membro weak_ptr<Y> m_stub no class Y. Em seguida, escreva:

shared_ptr<Y> Y::f()
{
    return m_stub.lock();
}

É útil quando você não pode alterar a classe que você está decorrentes (por exemplo, estendendo biblioteca de outras pessoas). Não se esqueça de inicializar o membro, por exemplo, por m_stub = shared_ptr<Y>(this), a sua é ainda válida durante um construtor.

É OK se há mais canhotos como este na hierarquia de herança, não vai evitar a destruição do objeto.

Editar: Como corretamente apontado por Nobar usuário, o código seria destruir Y objeto quando o serviço estiver terminado e variáveis ??temporárias são destruídos. Portanto, a minha resposta é incorreta.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top