Qual è l'utilità di `enable_shared_from_this`?
-
23-08-2019 - |
Domanda
mi sono imbattuto in enable_shared_from_this
durante la lettura degli esempi Boost.Asio e dopo aver letto la documentazione Sto ancora perso per come questo dovrebbe essere utilizzato in modo corretto. Qualcuno può per favore mi dia un esempio e / o e la spiegazione di quando si utilizza questa classe ha un senso.
Soluzione
E 'consente di ottenere un'istanza shared_ptr
valida per this
, quando tutto ciò che hai è this
. Senza di essa, si avrebbe alcun modo di ottenere un shared_ptr
a this
, a meno che non hai già avuto uno come membro. Questo esempio dalla documentazione spinta per 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
}
Il metodo f () restituisce un shared_ptr
valida, anche se non aveva alcuna istanza membro. Si noti che non si può semplicemente fare questo:
class Y: public enable_shared_from_this<Y>
{
public:
shared_ptr<Y> f()
{
return shared_ptr<Y>(this);
}
}
Il puntatore condivisa che questa tornata avrà un conteggio di riferimento diverso da quello "corretto" uno, e uno di loro finirà per perdere e in possesso di un riferimento penzoloni quando l'oggetto viene eliminato.
enable_shared_from_this
è diventata parte del C ++ 11 standard. È inoltre possibile ottenere da lì oltre che da spinta.
Altri suggerimenti
dall'articolo Dr Dobbs su puntatori deboli, penso che questo esempio è più facile da capire (fonte: http: // drdobbs .com / cpp / 184402026 ):
... il codice come questo non funziona correttamente:
int *ip = new int;
shared_ptr<int> sp1(ip);
shared_ptr<int> sp2(ip);
Nessuno dei due oggetti shared_ptr
sa circa l'altra, in modo che entrambi cercheranno di rilasciare la risorsa quando vengono distrutti. Che di solito porta a problemi.
Allo stesso modo, se una funzione di membro ha bisogno di un oggetto shared_ptr
che possiede l'oggetto che viene chiamato in poi, è possibile non solo creare un oggetto al volo:
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;
}
Questo codice ha lo stesso problema come l'esempio precedente, anche se in una forma più sottile. Quando si è costruito, l'oggetto shared_pt
sp1
r proprietario della risorsa appena allocato. Il codice all'interno della funzione membro S::dangerous
non sa su quell'oggetto shared_ptr
, quindi l'oggetto shared_ptr
che restituisce distinto da sp1
. Copiare il nuovo oggetto shared_ptr
a sp2
non aiuta; quando sp2
va fuori del campo di applicazione, che rilascerà la risorsa, e quando sp1
va fuori del campo di applicazione, che rilascerà nuovamente la risorsa.
Il modo per evitare questo problema è quello di utilizzare la enable_shared_from_this
modello di classe. Il modello prende un argomento di tipo di modello, che è il nome della classe che definisce la risorsa gestita. Quella classe deve, a sua volta, essere derivato pubblicamente dal modello; in questo modo:
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 si esegue questa operazione, tenere a mente che l'oggetto su cui si chiama shared_from_this
deve essere di proprietà di un oggetto shared_ptr
. Questo non funziona:
int main()
{
S *p = new S;
shared_ptr<S> sp2 = p->not_dangerous(); // don't do this
}
Ecco la mia spiegazione, dal punto di vista dadi e bulloni (risposta in alto non ha 'click' con me). * Si noti che questo è il risultato di indagare la fonte per shared_ptr e enable_shared_from_this che viene fornito con Visual Studio 2012. Forse altri compilatori implementano enable_shared_from_this diversamente ... *
enable_shared_from_this<T>
aggiunge un'istanza weak_ptr<T>
privato a T
che detiene il ' unico vero riferimento count ' per l'istanza di T
.
Quindi, quando si crea prima un shared_ptr<T>
su un nuovo T *, che weak_ptr interna T * s 'viene inizializzato con un refcount di 1. Il nuovo shared_ptr
appoggia sostanzialmente su questo weak_ptr
.
T
può allora, nei suoi metodi, chiamare shared_from_this
per ottenere un'istanza di shared_ptr<T>
che capiscono sullo stesso riferimento memorizzato internamente conteggio . In questo modo, si ha sempre un posto in cui ref-count di T*
viene memorizzato invece di avere più istanze shared_ptr
che non conoscono l'un l'altro, e ognuno pensa che sono il shared_ptr
che si occupa di ref-conteggio T
e l'eliminazione quando loro ref-count raggiunge lo zero.
Si noti che l'utilizzo di un boost :: intrusive_ptr non soffrono di questo problema. Questo è spesso un modo più conveniente per aggirare questo problema.
E 'esattamente lo stesso in C ++ 11 e versioni successive:. E' per attivare la capacità di restituire this
come puntatore condiviso dal this
ti dà un puntatore prime
In altre parole, si consenta di attivare il codice come questo
class Node {
public:
Node* getParent const() {
if (m_parent) {
return m_parent;
} else {
return this;
}
}
private:
Node * m_parent = nullptr;
};
in questo modo:
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;
};
Un altro modo è quello di aggiungere un membro weak_ptr<Y> m_stub
nella class Y
. Quindi scrivere:
shared_ptr<Y> Y::f()
{
return m_stub.lock();
}
Utile quando non è possibile modificare la classe che si sta derivanti da (ad esempio estendendo biblioteca di altre persone). Non dimenticate di inizializzare il membro, ad esempio, da m_stub = shared_ptr<Y>(this)
, la sua è valida anche durante un costruttore.
E 'OK se ci sono più stub come questa in gerarchia di ereditarietà, non impedirà distruzione dell'oggetto.
Modifica Come giustamente rilevato dalla Nobar dell'utente, il codice distruggerebbe oggetto Y quando il compito è finito e variabili temporanee vengono distrutti. Quindi la mia risposta non è corretta.