2015-12-10 22 views

risposta

1

No, non è sicuro. Dovresti chiamare solo shared_from_this se l'oggetto è gestito da un shared_ptr, non allocato tramite new (senza uno shared_ptr associato). Per esempio questo codice

struct Test: std::enable_shared_from_this<Test> { 
    std::shared_ptr<Test> getptr() { 
    return shared_from_this(); 
    } 
}; 

Test *test = new Test; 
std::shared_ptr<Test> test2 = test->getptr(); 

getterà std::bad_weak_ptr (almeno quando si utilizza libstdc++). Ma questo è OK:

std::shared_ptr<Test> test(new Test); 
std::shared_ptr<Test> test2 = test->getptr(); 
+0

Cosa succede con test2 quando inizio a gestire un nuovo puntatore con test dopo la chiamata a test-> getptr(). È un caso d'uso sicuro? Lo sto chiedendo perché boost :: asio utilizza molti di questi costrutti quando avvia operazioni di ricezione o scrittura asincrone. – Gustavo

+0

@Gustavo Non è possibile iniziare a gestire il puntatore dopo una chiamata a 'getptr' perché quest'ultimo verrà lanciato a meno che non sia già gestito (da una shared_ptr). – vitaut

+0

Intendo il seguente caso: std :: shared_ptr test (nuovo test); std :: shared_ptr test2 = test-> getptr(); test.reset (new AnotherTest); /* Fai qualcosa con test2 */ È un caso d'uso valido? – Gustavo

3

dalla norma:

§ 20.8.2.4

shared_from_this shared_ptr();

shared_ptr shared_from_this() const;

7 * Richiede: enable_shared_from_this sono una classe base accessibile di T. questo deve essere un subobject di un t oggetto di tipo T. È previsto almeno un shared_ptr esempio p che possiede & t.

8 Restituzioni: Un oggetto shared_ptr r che condivide la proprietà con p.

9 Postcondizioni: r.get() == questo

Se si chiama shared_from_this() all'interno di una classe che non è gestito da un shared_ptr il risultato sarà comportamento indefinito perché non avete rispettato una delle le precondizioni documentate del metodo.

So per esperienza che in [la versione corrente di] libC++ il risultato è un'eccezione generata. Tuttavia, come tutti i comportamenti non definiti , questo non deve essere fatto valere.

2

La documentazione di questa classe base dice che dovrebbe esistere un puntatore condiviso che possiede questo [oggetto] prima di chiamare shared_from_questo.

Ok, bello.

È sicuro allocare [oggetto] con new e chiamare shared_from_this per gestire l'oggetto?

No. Ci dovrebbe essere un puntatore condiviso che possiede questo [oggetto] prima di chiamare shared_from_questo.

1

Come già menzionato da altri utenti, le chiamate a shared_from_this su istanze non di proprietà di shared_ptr s provocheranno un comportamento non definito (di solito un'eccezione, ma non ci sono garanzie).

Quindi, perché un'altra risposta?

Perché io ho fatto la stessa domanda una volta ed ho ottenuto quasi la stessa risposta, poi ho iniziato alle prese con un'altra domanda che subito sorto dopo che - come posso garantire in tal modo che tutte le istanze sono gestite da un shared_ptr?

Per motivi di completezza, aggiungo un'altra risposta con alcuni dettagli su questo aspetto.
Ecco una soluzione semplice che non era stata menzionata prima.

Così semplice una soluzione, anzi: costruttori privati ​​, metodo factory e modello variadic s.
Ne consegue un frammento che mescola tutti insieme in un esempio minimo:

#include<memory> 
#include<utility> 

class C: public std::enable_shared_from_this<C> { 
    C() = default; 
    C(const C &) = default; 
    C(C &&) = default; 
    C& operator=(const C &) = default; 
    C& operator=(C &&c) = default; 

public: 
    template<typename... Args> 
    static std::shared_ptr<C> create(Args&&... args) noexcept { 
     return std::shared_ptr<C>{new C{std::forward<Args>(args)...}}; 
    } 

    std::shared_ptr<C> ptr() noexcept { 
     return shared_from_this(); 
    } 
}; 

int main() { 
    std::shared_ptr<C> c1 = C::create(); 
    std::shared_ptr<C> c2 = C::create(*c1); 
    std::shared_ptr<C> c3 = c2->ptr(); 
    // these won't work anymore... 
    // C c4{}; 
    // std::shared_ptr<C> c5 = std::make_shared<C>(); 
    // std::shared_ptr<C> c6{new C{}}; 
    // C c7{*c1}; 
    // ... and so on ... 
} 

L'(? Banale) idea di base è quella di vietare la costruzione esplicita di nuove istanze, ma utilizzando il metodo factory qui chiamato create .
I modelli variadici sono usati per evitare di scrivere diversi metodi di fabbrica, niente di più. L'inoltro perfetto ci aiuta a farlo nel modo giusto.

Abbastanza semplice, non è vero?
Ad ogni modo mi ci è voluto un po 'per capirlo, quindi spero che questo aiuti i futuri lettori a superare lo stesso dubbio.

Problemi correlati