2015-12-06 11 views
16

Attualmente sto usando alcune funzioni dalla libreria di glib. Con glib arriva anche il gio. glib è una libreria C e quindi ho bisogno di cancellare alcune strutture che creo.Come utilizzare un shared_ptr con un puntatore a struct che non dovrebbe essere liberato

per molti degli oggetti che creo una smart pointer ad esempio:

std::shared_ptr<GAsyncQueue> my_queue = std::shared_ptr<GAsyncQueue>(g_async_queue_create(), g_async_queue_unref); 

Per questo crea un puntatore condiviso a un GAsyncQueue e questo è distrugge in modo sicuro la coda alla sua fine della sua vita.

Tuttavia, si verifica un problema quando ottengo un puntatore dalla libreria gio che non dovrei liberare. Nel seguente codice my_connection è un GSocketClient che implementa (in un linguaggio parlante) GIOStream.

std::shared_ptr<GInputStream> my_input_stream = 
    std::shared_ptr<GInputStream> (
     g_io_stream_get_input_stream(G_IO_STREAM(my_connection.get())) 
    ); 

Poiché la documentazione sul GIOStream menziona, che il puntatore ottenuto con g_io_stream_get_input_stream() non deve essere liberato. Questo perché è di proprietà dell'istanza my_connection. Ho pensato di creare una lamda per l'oggetto destroy, il secondo parametro di un oggetto pointer condiviso. es. auto deleter = [](GInputStream* ptr) {}; e poi dare quel lambda come funzione detroy al puntatore condiviso, ma questo sembra una specie di stupido.

+3

Perché utilizzare un puntatore (intelligente)? Non sarebbe sufficiente una referenza? – edmz

+0

@black ci sto ancora riflettendo. Il flusso di input è un'istanza di un oggetto che viene copiato. GIOStream viene distrutto quando viene chiamato l'ultimo distruttore di copia. Forse dal momento che non ho bisogno di distruggerlo comunque, è anche un semplice puntatore vecchio ... – hetepeperfan

+0

E un puntatore posso impostare su NULL, quindi è più facile vedere se è inizializzato. – hetepeperfan

risposta

12

Beh, alternativa al no-op deleter potrebbe utilizzare aliasing condiviso puntatore

template <class U> shared_ptr (const shared_ptr<U>& x, element_type* p) noexcept; 

Condivide x, ma dopo get() si otterrà indietro p.

Discussione: What is shared_ptr's aliasing constructor for?

5

è possibile utilizzare un tipo di delezione che non fa nulla, ma dovranno essere passato come argomento al costruttore 's il shared_ptr

struct DoNothing { 
    template <typename T> 
    void operator()(T*) const noexcept { } 
}; 

Quando si crea un shared_ptr è necessario creare uno di questi hanno eliminato e passalo nel costruttore (come stai facendo con il lambda). È possibile rendere questo più facile su te stesso con una funzione intermedia

template <typename T> 
std::shared_ptr<T> non_deleting_shared_ptr(T* ptr) { 
    return {ptr, DoNothing}; 
} 

auto my_input_stream = 
    non_deleting_shared_ptr(
     g_io_stream_get_input_stream(G_IO_STREAM(my_connection.get())); 

Tuttavia, la domanda più importante è il motivo per cui stai usando puntatori intelligenti quando non si desidera la proprietà di essere una parte di esso. Quasi sicuramente starai meglio con un GAsyncQueue*, a meno che tu non sia in una situazione in cui hai un shared_ptr che deve liberare a volte. Come un membro dei dati forse?

6

Probabilmente solo non hanno bisogno di un std::shared_ptr. E probabilmente non hai nemmeno bisogno di un puntatore.

Come ho letto la tua domanda e commenti, non vedo alcun punto contro

auto& my_input_stream = *(g_io_stream_get_input_stream(G_IO_STREAM(my_connection.get()))) 

E 'vero che i puntatori consentono dati facoltativi. Tuttavia, è anche vero che è usato principalmente nel modo sbagliato. Avere

void foo(type* ptr) 
{ 
    if (!ptr) 
     throw exception; 
} 

spesso non ha senso. Se la funzione deve lavorare su dati concreti, l'abilitazione di un parametro NULL è utile solo se ti preoccupi di fornire tali dati.Altrimenti, basta richiedere un riferimento (possibilmente const) all'oggetto.

I puntatori intelligenti sono utili; ma sono ancora puntatori. Evitarli del tutto, se possibile, è ancora meglio.


Dai commenti:

Tuttavia, un punto di riferimento deve sempre essere inizializzato

Assolutamente. Dal momento che C + + 11, tuttavia, abbiamo std::reference_wrapper che può anche essere analizzato e memorizzato in contenitori.

+0

Sulla base della mia domanda hai ragione. Tuttavia, un riferimento deve sempre essere inizializzato. Poiché my_input_stream è un membro della classe (non mostrato nella mia domanda) che non è aperto. Posso impostarlo solo quando effettivamente apro la connessione socket. Ma lo inviterò perché è una buona risposta per gli altri lettori. – hetepeperfan

+0

@hetepeperfan True. Verifica se il mio aggiornamento può risolverlo;) – edmz

Problemi correlati