2009-12-02 16 views
21

Eventuali duplicati:
How to release pointer from boost::shared_ptr?Staccare un puntatore da un shared_ptr?

Una funzione di mia interfaccia restituisce un puntatore ad un oggetto. L'utente dovrebbe assumere la proprietà di tale oggetto. Non voglio restituire un Boost.shared_ptr, perché non voglio forzare i client a usare boost. Tuttavia, internamente, vorrei memorizzare il puntatore in un parametro shared_ptr per evitare perdite di memoria in caso di eccezioni, ecc. Sembra che non ci sia modo di staccare un puntatore da un puntatore condiviso. Qualche idea qui?

+0

È un'opzione per restituire una copia dell'oggetto condiviso e lasciare che il puntatore intelligente ripulisca l'originale? – TimW

+0

Non proprio, l'oggetto è un videoregistratore con risoluzione HD, quindi la copia è troppo costosa. –

+0

Si noti che C++ 11 ha std :: unique_ptr che può essere utilizzato per memorizzare temporaneamente il puntatore e rilasciarlo al ritorno. – Adversus

risposta

23

Quello che stai cercando è una funzione release; shared_ptr non ha una funzione di rilascio. Per the Boost manual:

Q. Perché shared_ptr non fornisce una funzione release()?

A. shared_ptr non può assegnare la proprietà a meno che non sia univoco() perché l'altra copia continuerà a distruggere l'oggetto.

considerare:

shared_ptr<int> a(new int); 
shared_ptr<int> b(a); // a.use_count() == b.use_count() == 2 

int * p = a.release(); 

// Who owns p now? b will still call delete on it in its destructor. 

Inoltre, il puntatore restituito da release() sarebbe difficile da rilasciare in modo affidabile, come lo shared_ptr fonte potrebbe essere stato creato con un deleter personalizzato.

Due opzioni che si potrebbe prendere in considerazione:

  • si potrebbe usare std::tr1::shared_ptr, che richiederebbe agli utenti di utilizzare un'implementazione C++ librerie di supporto TR1 o usare Boost; almeno questo darebbe loro l'opzione tra i due.
  • È possibile implementare il proprio puntatore condiviso boost::shared_ptr e utilizzarlo sulle interfacce esterne.

Si potrebbe anche guardare la discussione a questa domanda su using boost::shared_ptr in a library's public interface.

+0

Ok, temevo che potesse essere qualcosa del genere. –

+5

shared_ptr è un po 'come const keyword, aggiungilo in un posto e devi usarlo ovunque –

+0

Lettori: Scorri verso il basso: [Crea il tuo deleter] (http://stackoverflow.com/a/5995770/321013) –

2

Come James ha ben coperto, non è possibile staccare un puntatore condiviso.

Avete bisogno di più proprietari internamente oppure trasferite la proprietà dalla vostra classe al cliente? In tal caso un std::auto_ptr potrebbe adattarsi alla bolletta.

Se siete preoccupati per la semantica sorprendenti std::auto_ptr, si potrebbe tenere internamente da boost::scoped_ptr, e staccarlo in corrispondenza del punto di consegnare fuori - lasciando al cliente per eliminare manualmente o conservarla in loro proprio puntatore intelligente.

Se si dispone di più proprietari dalla propria parte, è possibile utilizzare un conteggio intrusivo. Internamente è quindi possibile utilizzare boost::intrusive__ptr, ma passare il puntatore raw all'interfaccia.Il client può quindi lavorare manualmente con i conteggi ref, o conservarla in un boost::intrusive_ptr se stessi (ma non farli dipendono da esso)

11

L'utente dovrebbe assumere la proprietà di tale oggetto. Non voglio tornare un Boost.shared_ptr,

shared_ptr esprime condiviso proprietà, e volete che il vostro interfaccia per esprimere trasferimento di proprietà. std::auto_ptr sarebbe quindi più applicabile qui.

Internamente però, vorrei memorizzare il puntatore in una shared_ptr per evitare perdite di memoria in caso di eccezioni

Anche in questo caso, shared_ptr non può essere lo strumento migliore per quel lavoro. Per evitare perdite in caso di eccezioni, scoped_ptr o auto_ptr sarebbe più adatto.

22

c'è sempre un modo :-)

V'è infatti una ragione per cui essi non forniscono un metodo di rilascio(), ma non è impossibile crearne uno. Crea il tuo personale deleter. Qualcosa sulla linea del (non hanno effettivamente compilato il codice, ma questa è la nozione generale):

template <typename T> 
class release_deleter{ 
public: 
    release_deleter() : released_(new some_atomic_bool(false)){} 
    void release() {released_->set(true);} 
    void operator()(T* ptr){if(!released_->get()) delete ptr;} 
private: 
    shared_ptr<some_atomic_bool> released_; 
} 

.. 

shared_ptr<some_type> ptr(new some_type, release_deleter<some_type>()); 

.. 

release_deleter<some_type>* deleter = get_deleter<release_deleter<some_type>>(ptr); 
deleter->release(); 
some_type* released_ptr = ptr.get(); 
+0

Perché è il membro 'released_' privato a shared_ptr? –

+1

Mi stavo chiedendo la stessa cosa. L'ho fatto come un normale bool e si adattava perfettamente alle mie esigenze. – David

+0

Nessun motivo. Non avevo effettivamente esaminato il codice, ma ora vedo che Deleter è memorizzato con shared_count, quindi non sarebbe necessaria alcuna condivisione aggiuntiva. – Magnus

4

Utilizzare un shared_ptr ad un scoped_ptr alla risorsa (shared_ptr<scoped_ptr<Resource>>). In questo modo si ottiene il conteggio dei riferimenti di shared_ptr, che distruggerà automaticamente la risorsa se e solo se è ancora collegata allo scoped_ptr. Ma puoi scollegare lo scoped_ptr quando sei pronto a cedere la proprietà.

+0

Intelligente, ma ora devi cambiare * p a ** p ogni dove. Questo mostra un problema con il progetto 'shared_ptr'. – curiousguy

+2

@curiousguy: Quanto spesso usi '*' su un puntatore intelligente? Di solito si usa '->', che ha una semantica concatenata. E comunque, la stessa sintassi esatta non era richiesta nella domanda, e questo ha il vantaggio di funzionare esattamente come l'OP voleva. –

+1

"_Come spesso usi * su un puntatore intelligente?_ "Non lo so." _Usually you use ->, che ha incatenato la semantica ._ "Intelligente, l'ho dimenticato, ma non mi piace la doppia indirezione: poiché la mancanza di' release' è solo una mancanza di 'shared_ptr' design, quindi non vorrei cambiare il codice che lo usa solo per questo motivo, preferisco aggirare la limitazione arbitraria cambiando il deleter: anche questo è un'aggiunta indiretta, ma non influenza il codice che usa il puntatore intelligente. (Ma in realtà preferirei scrivere il mio puntatore intelligente senza limiti 'shared_ptr'). – curiousguy

Problemi correlati