Qual é a utilidade de `enable_shared_from_this`?
-
23-08-2019 - |
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.
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_pt
r 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.