Risposte:
È facile quando si dispone di proprietà che è possibile assegnare a ciascun puntatore intelligente. Esistono tre proprietà importanti.
Il primo significa che un puntatore intelligente non può eliminare l'oggetto, perché non lo possiede. Il secondo significa che solo un puntatore intelligente può mai puntare allo stesso oggetto contemporaneamente. Se il puntatore intelligente deve essere restituito dalle funzioni, ad esempio, la proprietà viene trasferita al puntatore intelligente restituito.
Il terzo significa che più puntatori intelligenti possono puntare allo stesso oggetto contemporaneamente. Questo vale anche per un puntatore non elaborato, tuttavia i puntatori non elaborati mancano di una funzione importante: non definiscono se possiedono o meno. Una condivisione di proprietà del puntatore intelligente eliminerà l'oggetto se ogni proprietario rinuncia all'oggetto. Questo comportamento sembra essere necessario spesso, quindi i puntatori intelligenti di proprietà condivisa sono ampiamente diffusi.
Alcuni proprietari di puntatori intelligenti non supportano né il secondo né il terzo. Non possono quindi essere restituiti da funzioni o passati altrove. Per quale è più adattoRAII
scopi in cui il puntatore intelligente viene mantenuto locale ed è appena creato in modo da liberare un oggetto dopo che è uscito dall'ambito.
La quota di proprietà può essere implementata con un costruttore di copie. Questo naturalmente copia un puntatore intelligente e sia la copia che l'originale faranno riferimento allo stesso oggetto. Il trasferimento di proprietà al momento non può essere realmente implementato in C ++, poiché non vi sono mezzi per trasferire qualcosa da un oggetto a un altro supportato dalla lingua: se si tenta di restituire un oggetto da una funzione, ciò che sta accadendo è che l'oggetto viene copiato. Quindi un puntatore intelligente che implementa il trasferimento di proprietà deve usare il costruttore di copie per implementare quel trasferimento di proprietà. Tuttavia, questo a sua volta interrompe il suo utilizzo in contenitori, poiché i requisiti stabiliscono un determinato comportamento del costruttore di copie di elementi di contenitori che è incompatibile con il cosiddetto comportamento "costruttore mobile" di questi puntatori intelligenti.
C ++ 1x fornisce supporto nativo per il trasferimento di proprietà introducendo i cosiddetti "costruttori di spostamento" e "operatori di assegnazione di spostamenti". Viene fornito anche con un puntatore intelligente di trasferimento di proprietà chiamato unique_ptr
.
scoped_ptr
è un puntatore intelligente che non è né trasferibile né condivisibile. È utilizzabile solo se è necessario localmente allocare memoria, ma assicurarsi che venga liberato di nuovo quando non rientra nell'ambito. Ma può ancora essere scambiato con un altro scoped_ptr, se lo si desidera.
shared_ptr
è un puntatore intelligente che condivide la proprietà (terzo tipo sopra). Viene contato come riferimento in modo che possa vedere quando l'ultima copia di esso esce dall'ambito e quindi libera l'oggetto gestito.
weak_ptr
è un puntatore intelligente non proprietario. Viene utilizzato per fare riferimento a un oggetto gestito (gestito da shared_ptr) senza aggiungere un conteggio dei riferimenti. Normalmente, è necessario estrarre il puntatore non elaborato da shared_ptr e copiarlo. Ma questo non sarebbe sicuro, in quanto non avresti un modo per verificare quando l'oggetto è stato effettivamente eliminato. Quindi, weak_ptr fornisce mezzi facendo riferimento a un oggetto gestito da shared_ptr. Se è necessario accedere all'oggetto, è possibile bloccarne la gestione (per evitare che in un altro thread un shared_ptr lo liberi mentre si utilizza l'oggetto) e quindi utilizzarlo. Se weak_ptr punta a un oggetto già eliminato, ti noterà lanciando un'eccezione. L'uso di weak_ptr è molto utile quando si ha un riferimento ciclico: il conteggio dei riferimenti non può facilmente far fronte a una situazione del genere.
intrusive_ptr
è simile a shared_ptr ma non mantiene il conteggio dei riferimenti in shared_ptr ma lascia incrementare / decrementare il conteggio per alcune funzioni di supporto che devono essere definite dall'oggetto gestito. Ciò ha il vantaggio che un oggetto già referenziato (che ha un conteggio dei riferimenti incrementato da un meccanismo di conteggio dei riferimenti esterno) può essere inserito in un intrusive_ptr - perché il conteggio dei riferimenti non è più interno al puntatore intelligente, ma il puntatore intelligente utilizza un esistente meccanismo di conteggio dei riferimenti.
unique_ptr
è un trasferimento del puntatore di proprietà. Non puoi copiarlo, ma puoi spostarlo usando i costruttori di spostamento di C ++ 1x:
unique_ptr<type> p(new type);
unique_ptr<type> q(p); // not legal!
unique_ptr<type> r(move(p)); // legal. p is now empty, but r owns the object
unique_ptr<type> s(function_returning_a_unique_ptr()); // legal!
Questa è la semantica a cui obbedisce std :: auto_ptr, ma a causa della mancanza del supporto nativo per lo spostamento, non riesce a fornirli senza insidie. unique_ptr ruberà automaticamente risorse da un altro unique_ptr temporaneo che è una delle caratteristiche chiave della semantica di spostamento. auto_ptr sarà deprecato nella prossima versione C ++ Standard a favore di unique_ptr. C ++ 1x consentirà inoltre oggetti di riempimento che sono solo mobili ma non copiabili in contenitori. Quindi, per esempio, puoi inserire unique_ptr in un vettore. Mi fermo qui e ti rimando a un bell'articolo su questo se vuoi leggere di più su questo.
auto_ptr
è già obsoleto (C ++ 11).
intrusive_ptr
può essere preferibile farlo shared_ptr
per una migliore coerenza della cache. Apparentemente la cache funziona meglio se si memorizza il conteggio dei riferimenti come parte della memoria dell'oggetto gestito stesso anziché di un oggetto separato. Questo può essere implementato in un modello o in una superclasse dell'oggetto gestito.
scoped_ptr è il più semplice. Quando esce dal campo di applicazione, viene distrutto. Il seguente codice è illegale (scoped_ptrs non è copiabile) ma illustrerà un punto:
std::vector< scoped_ptr<T> > tPtrVec;
{
scoped_ptr<T> tPtr(new T());
tPtrVec.push_back(tPtr);
// raw T* is freed
}
tPtrVec[0]->DoSomething(); // accessing freed memory
shared_ptr è conteggiato come riferimento. Ogni volta che si verifica una copia o un'assegnazione, il conteggio dei riferimenti viene incrementato. Ogni volta che viene sparato un distruttore di un'istanza, il conteggio dei riferimenti per il T * grezzo viene diminuito. Una volta che è 0, il puntatore viene liberato.
std::vector< shared_ptr<T> > tPtrVec;
{
shared_ptr<T> tPtr(new T());
// This copy to tPtrVec.push_back and ultimately to the vector storage
// causes the reference count to go from 1->2
tPtrVec.push_back(tPtr);
// num references to T goes from 2->1 on the destruction of tPtr
}
tPtrVec[0]->DoSomething(); // raw T* still exists, so this is safe
weak_ptr è un riferimento debole a un puntatore condiviso che richiede di verificare se il punto_partecipato_ptr è ancora in giro
std::vector< weak_ptr<T> > tPtrVec;
{
shared_ptr<T> tPtr(new T());
tPtrVec.push_back(tPtr);
// num references to T goes from 1->0
}
shared_ptr<T> tPtrAccessed = tPtrVec[0].lock();
if (tPtrAccessed[0].get() == 0)
{
cout << "Raw T* was freed, can't access it"
}
else
{
tPtrVec[0]->DoSomething(); // raw
}
intrusive_ptr viene in genere utilizzato quando è presente un smart ptr di terze parti che è necessario utilizzare. Chiamerà una funzione gratuita per aggiungere e decrementare il conteggio dei riferimenti. Vedi il link per aumentare la documentazione per maggiori informazioni.
if (tPtrAccessed[0].get() == 0)
dovrebbe essere if (tPtrAccessed.get() == 0)
?
Non trascurare boost::ptr_container
in alcun sondaggio di boost puntatori intelligenti. Possono essere preziosi in situazioni in cui un esempio std::vector<boost::shared_ptr<T> >
sarebbe troppo lento.
Seguo il consiglio di guardare la documentazione. Non è così spaventoso come sembra. E alcuni brevi suggerimenti:
scoped_ptr
- un puntatore viene automaticamente eliminato quando esce dal campo di applicazione. Nota: nessuna assegnazione possibile, ma non comporta costi generaliintrusive_ptr
- puntatore conteggio dei riferimenti senza sovraccarico di smart_ptr
. Tuttavia, l'oggetto stesso memorizza il conteggio dei riferimentiweak_ptr
- collabora shared_ptr
con le situazioni che comportano dipendenze circolari (leggi la documentazione e cerca su Google una bella immagine;)shared_ptr
- il generico, il più potente (e pesante) dei puntatori intelligenti (da quelli offerti da boost)auto_ptr
, che assicura che l'oggetto a cui punta viene distrutto automaticamente quando il controllo lascia un ambito. Tuttavia ha una semantica della copia diversa rispetto al resto dei ragazzi.unique_ptr
- verrà fornito con C ++ 0xRisposta alla modifica: Sì