Ele permite que você obtenha uma shared_ptr
instância válida para this
quando tudo o que você tem é this
. Sem ele, você não teria nenhuma maneira de obter um shared_ptr
para this
, a menos que você já teve um como um membro. Este exemplo da documentação do impulso 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 válido shared_ptr
, mesmo que não tivesse instância de membro. Observe 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 compartilhado que isso retornou terá uma contagem de referência diferente da "adequada" e uma delas acabará perdendo e mantendo uma referência pendente quando o objeto for excluído.
enable_shared_from_this
tornou-se parte do padrão C ++ 11. Você também pode obtê-lo de lá, bem como do impulso.
std::shared_ptr
construtor em um ponteiro bruto, se herdar destd::enable_shared_from_this
. Não sei se a semântica do Boost foi atualizada para suportar isso.std::shared_ptr
para um objeto que já é gerenciado por outrostd::shared_ptr
não consultará a referência fraca armazenada internamente e, portanto, levará a um comportamento indefinido". ( pt.cppreference.com/w/cpp/memory/enable_shared_from_this )shared_ptr<Y> q = p
?std::make_shared<T>
.do artigo do Dr. Dobbs sobre indicadores fracos, acho que este exemplo é mais fácil de entender (fonte: http://drdobbs.com/cpp/184402026 ):
... código como este não funcionará corretamente:
Nenhum dos dois
shared_ptr
objetos conhece o outro, portanto, ambos tentarão liberar o recurso quando forem destruídos. Isso geralmente leva a problemas.Da mesma forma, se uma função membro precisar de um
shared_ptr
objeto que possua o objeto em que está sendo chamada, ela não poderá simplesmente criar um objeto em tempo real:Este código tem o mesmo problema que o exemplo anterior, embora de uma forma mais sutil. Quando é construído, o
shared_pt
objeto rsp1
possui o recurso recém-alocado. O código dentro da função de membroS::dangerous
não sabe sobre esseshared_ptr
objeto, portanto, oshared_ptr
objeto que ele retorna é distintosp1
. Copiar o novoshared_ptr
objeto parasp2
não ajuda; quandosp2
sai do escopo, ele libera o recurso e, quandosp1
sai do escopo, libera o recurso novamente.A maneira de evitar esse problema é usar o modelo de classe
enable_shared_from_this
. O modelo usa um argumento de tipo de modelo, que é o nome da classe que define o recurso gerenciado. Essa classe, por sua vez, deve ser derivada publicamente do modelo; como isso:Ao fazer isso, lembre-se de que o objeto no qual você chama
shared_from_this
deve pertencer a umshared_ptr
objeto. Isso não vai funcionar:fonte
shared_ptr<S> sp1(new S);
ser preferível usá-loshared_ptr<S> sp1 = make_shared<S>();
, consulte por exemplo stackoverflow.com/questions/18301511/…shared_ptr<S> sp2 = p->not_dangerous();
porque a armadilha aqui é que você deve criar um shared_ptr da maneira normal antes de ligarshared_from_this()
pela primeira vez! É realmente fácil errar! Antes do C ++ 17, é UB chamarshared_from_this()
antes que exatamente um shared_ptr tenha sido criado da maneira normal:auto sptr = std::make_shared<S>();
oushared_ptr<S> sptr(new S());
. Felizmente, a partir do C ++ 17, isso será lançado.S* s = new S(); shared_ptr<S> ptr = s->not_dangerous();
<- É permitido chamar shared_from_this apenas em um objeto compartilhado anteriormente, ou seja, em um objeto gerenciado por std :: shared_ptr <T>. Caso contrário, o comportamento é indefinido (até C ++ 17) std :: bad_weak_ptr é lançado (pelo construtor shared_ptr a partir de um fraco_estilo construído por padrão) (desde C ++ 17). . Portanto, a realidade é que deve ser chamadaalways_dangerous()
, porque você precisa saber se já foi compartilhado ou não.Aqui está minha explicação, do ponto de vista de porcas e parafusos (a resposta principal não 'clicou' comigo). * Observe que esse é o resultado da investigação da origem do shared_ptr e do enable_shared_from_this fornecido com o Visual Studio 2012. Talvez outros compiladores implementem o enable_shared_from_this de maneira diferente ... *
enable_shared_from_this<T>
adiciona um privadoweak_ptr<T>
instância àT
qual contém a ' única contagem de referência verdadeira ' para a instância deT
.Então, quando você cria um
shared_ptr<T>
para um novo T *, o ponto fraco_ptr interno do T * é inicializado com uma contagem de ref 1. O novoshared_ptr
basicamente se baseia nissoweak_ptr
.T
Em seguida, em seus métodos, pode chamarshared_from_this
para obter uma instânciashared_ptr<T>
disso para a mesma contagem de referência armazenada internamente . Dessa forma, você sempre tem um lugar ondeT*
a contagem de ref é armazenada, em vez de ter váriasshared_ptr
instâncias que não se conhecem e cada uma pensa que é ashared_ptr
responsável pela contagem de refT
e excluí-la quando a ref -count chega a zero.fonte
So, when you first create...
porque isso é um requisito (como você diz que o fraca_ptr não é inicializado até você passar o ponteiro de objetos para um compartilhado_ptrador!) E esse requisito é onde as coisas podem dar terrivelmente errado se você estiver descuidado. Se você não criar um shared_ptr antes de ligar,shared_from_this
receberá o UB - da mesma forma, se você criar mais de um shared_ptr, também receberá o UB. Você precisa, de alguma forma, criar um shared_ptr exatamente uma vez.enable_shared_from_this
é frágil, pois o objetivo é obter umshared_ptr<T>
de aT*
, mas, na realidade, quando você recebe um ponteiroT* t
, geralmente não é seguro assumir que algo já está sendo compartilhado ou não, e fazer o palpite errado é UB.Observe que o uso de um boost :: intrusive_ptr não sofre esse problema. Geralmente, é uma maneira mais conveniente de contornar esse problema.
fonte
enable_shared_from_this
permite que você trabalhe com uma API que aceite especificamenteshared_ptr<>
. Na minha opinião, essa API geralmente está fazendo errado (como é melhor deixar que algo mais alto na pilha possua a memória), mas se você for forçado a trabalhar com essa API, essa é uma boa opção.É exatamente o mesmo no c ++ 11 e posterior: é para permitir a capacidade de retornar
this
como um ponteiro compartilhado, poisthis
fornece um ponteiro bruto.em outras palavras, permite transformar código como este
nisso:
fonte
shared_ptr
. Você pode alterar a interface para garantir que seja o caso.std::shared_ptr<Node> getParent const()
, eu normalmente o exporiaNodePtr getParent const()
. Se você absolutamente precisa acessar o ponteiro bruto interno (melhor exemplo: lidando com uma biblioteca C), éstd::shared_ptr<T>::get
isso que eu odeio mencionar porque eu tenho esse acessador de ponteiro bruto usado muitas vezes pelo motivo errado.Outra maneira é adicionar um
weak_ptr<Y> m_stub
membro aoclass Y
. Então escreva:Útil quando você não pode alterar a classe da qual você está derivando (por exemplo, estendendo a biblioteca de outras pessoas). Não se esqueça de inicializar o membro, por exemplo,
m_stub = shared_ptr<Y>(this)
, , é válido mesmo durante um construtor.Tudo bem se houver mais stubs como este na hierarquia de herança, isso não impedirá a destruição do objeto.
Editar: Conforme indicado corretamente pelo usuário nobar, o código destruirá o objeto Y quando a atribuição for concluída e as variáveis temporárias forem destruídas. Portanto, minha resposta está incorreta.
fonte
shared_ptr<>
que não apague seu pontapé, isso é um exagero. Você pode simplesmente dizerreturn shared_ptr<Y>(this, no_op_deleter);
ondeno_op_deleter
está um objeto de função unária que não aceitaY*
e não faz nada.m_stub = shared_ptr<Y>(this)
irá construir e destruir imediatamente um shared_ptr temporário disso. Quando essa instrução terminar,this
será excluída e todas as referências subseqüentes ficarão pendentes.enable_shared_from_this
, ela mantém umweak_ptr
de si (preenchido pelo ctor), retornado comoshared_ptr
quando você chamashared_from_this
. Em outras palavras, você está duplicando o queenable_shared_from_this
já fornece.