2013-08-27 16 views
5

Non è una domanda "come si fa" è piuttosto "come farlo nel modo giusto"C++ 11 riferimento/puntatore non proprietario a unique_ptr?

Sto sviluppando un editor in Qt dove diversi widget visualizzano i bambini e le sue variabili (membro). Ognuno di questi widget dovrebbe contenere un riferimento/puntatore al figlio modificato per visualizzare e modificare le sue variabili membro.

Il primo tentativo è stato il vecchio modo ANSI C che ho imparato (e ancora un po 'bloccato) con un semplice puntatore raw agli oggetti usati. Funziona bene, ma poiché lo standard C++ 11 supporta puntatore intelligente e si sta utilizzando è consigliabile che sto cercando di usarli.

Il problema è, io non sono abbastanza sicuro che cosa è il "modo migliore" per usarli in questo caso ... Dopo aver letto Smart Pointers: Or who owns you baby? e Which kind of pointer do I use when? e pochi altri sono arrivato a conclusioni diverse:

Il Il primo è usare un valore *unique_ptr poiché l'oggetto modificato è chiaramente il proprietario che crea e cancella anche i suoi figli. I widget si riferiscono semplicemente al bambino per mostrarlo o modificarlo. Il problema è come dovrebbero i widget fare riferimento al bambino ...

per ora sto semplicemente ancora utilizzando un indicatore grezzo ho ottenuto con il metodo della unique_ptrget() ma questo sembra un pò viziata per me. Posso ancora cancellare la chiamata accidentale sul puntatore e annullare i vantaggi del puntatore intelligente.

Il secondo approccio consiste nell'utilizzare uno shared_ptr perché molti oggetti si riferiscono al bambino e lo modificano. Anche accidentalmente cancellarlo in un widget non farebbe male perché è ancora di proprietà di altri oggetti. Il problema è che lo possiedono. Quando voglio cancellarlo dall'oggetto modificato devo anche segnalare tutti i widget per cancellarlo prima che sia davvero finito. (questo sembra di nuovo imperfetto e incline agli errori)

Non sono molto contento in entrambi i modi. C'è un modo (er) pulito di indicare il figlio dell'oggetto unique_ptr? O mi manca un approccio completamente diverso e migliore a questo problema?

+0

È necessario che gli oggetti di riferimento vengano avvisati quando l'oggetto referenziato è distrutto? –

+2

In questo tipo di situazioni, abbiamo davvero bisogno di [puntatori intelligenti stupidi] (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3514.pdf). –

+0

@BenjaminLindley Per ora devono essere avvisati di rimuovere i corrispondenti widget/controlli e di non usarli quando vengono cancellati. per esempio. un widget ha bisogno di ottenere le coordinate di un bambino per posizionarlo nel rendering di OpenGL. Ma cerco davvero di evitarlo perché ogni widget deve reagire al segnale emesso. Sarebbe più ideale se il proprietario potesse determinare se un oggetto esiste ancora o meno. – nils277

risposta

2

Si desidera utilizzare uno shared_ptr al posto del proprio unique_ptr e weak_ptr al posto dei puntatori grezzi. Questo darà esattamente quello che stai cercando. Gli weak_ptr non interferiranno con la capacità dell'oggetto modificato di eliminare l'oggetto sottostante.

+0

Grazie, sembra la cosa esatta che stavo cercando. – nils277

1

Il tuo caso d'uso non si traduce direttamente nei requisiti (cosa succede se qualcun altro cancella il widget mentre lo stai modificando?) Ma assumerò che non ti serva nulla oltre a un puntatore nudo.

La libreria standard non fornisce alcuna classe di osservatore puntatore rigorosa.Tra le entità Observer:

  • Nullable, puntatori mutevoli di tipo nativo (T *)
  • non nullable, riferimenti non mutabili di tipo nativo (T &)
  • non nullable, riferimenti mutevoli/proxy della classe tipo (std::reference_wrapper<T>)
  • Nullable, autolegittimante, puntatori mutabili a oggetti gestiti (std::weak_ptr<T>)

Se si desidera un puntatore non annullabile e mutabile a un oggetto non gestito, che è una cosa abbastanza ragionevole da volere, è possibile eseguire il rollover.

Ma i puntatori nudi non sono poi così male. L'unica differenza è che può essere nullptr e che non ha un nome lungo, elegante, all'interno del namespace std.

1

Se si utilizza Qt, si potrebbe considerare l'utilizzo dei puntatori intelligenti Qt, invece di std :: puntatori intelligenti:

o per QObject:

o per raggiungere la condivisione dei dati copy-on-write, contenitore di Qt e stile QString:

Non ci sono altre classi di puntatore anche, ma alcuni di quanto sopra sono più probabilità di Fai quello che vuoi. Inoltre, se avete i vostri dati nelle classi di contenitori Qt, QStrings o simili, gestiscono la loro memoria con semantica copy-on-write e generalmente devono essere passati come valori semplici (a volte come riferimenti) anziché come puntatori.

Ma, soprattutto, non utilizzare std::unique_ptr o std::shared_ptr con QObject che hanno i genitori, perché se il genitore cancella il primo figlio, poi lo std :: puntatore cancellerà ancora una volta, il programma di crash (altro modo funzionerà bene, il bambino comunicherà al genitore che è stato eliminato). In altre parole, ci sono buone possibilità di bug sottili se si mescolano QObjects e std :: pointer, quindi non farlo. Non è chiaro dalla tua domanda se lo stai facendo, dicendo per ogni evenienza.

+0

Hai un buon punto lì. Non mi aspettavo che 'QObject' avesse un problema con il puntatore std ::. Penso che 'QSharedPointer' e' QWeakPointer' sono la via da seguire per me, dal momento che i miei oggetti derivano da QObject per fare uso di segnali e slot. – nils277

+0

@ nill277 Si noti lo stesso problema con 'QSharedPointer', non ha alcun supporto speciale per' QObject' eliminato dal genitore. Se non c'è nessun oggetto genitore, allora va bene, ma in caso contrario, si applica la stessa cosa. Immagino che sia un modo giusto per farlo, se i tuoi QObjects non hanno alcun genitore/proprietario logico, fai attenzione a ricordarti di tenerlo in quel modo (ad esempio, non hai un costruttore che permetta di dare un genitore). Se hanno un genitore, usa semplicemente 'QPointer' per mantenere riferimenti deboli altrove. – hyde

0

C'è una proposta per il world's dumbest smart pointer, che è un puntatore non proprietario. Fondamentalmente, è un T* che dice "oh, e, a proposito, non rivendico alcuna proprietà su questi dati".

In questo caso, è necessario controllare che il puntatore osservatore/passivo/muto venga ripristinato se lo si spegne - metà della gestione della durata manuale. Utilizzando un raw T* con un nome che indica che non è proprietario funziona altrettanto bene prima di avere quanto sopra.

Ci sono vantaggi nel fare quanto sopra, specialmente quando si hanno oggetti con durata dipendente.

Se non lo fai, quindi un shared_ptr e weak_ptr funziona. Si noti, tuttavia, che chiunque abbia un weak_ptr può creare un nuovo shared_ptr, che potrebbe estendere la durata dell'oggetto condiviso oltre lo shared_ptr originale. Ciò deve accadere, poiché altrimenti esiste una condizione di competizione in cui l'utente di weak_ptr assicura che i suoi dati siano validi, quindi va a utilizzarlo, ma prima che eseguano i dati di shared_ptr viene distrutto.

Quindi il weak_ptr deve impedire l'eliminazione dello shared_ptr (blocco in contesto multi-thread, causando il "fail" in qualche modo in contesti a thread singolo). "Fail" in questo caso consiste nell'estensione della durata, che risolve sia il problema multi-thread che quello a thread singolo.

(Il singolo problema filettato è di verificare la weak_ptr è buono, qualcosa che provoca il shared_ptr per ripristinare, quindi continuano a voler utilizzare i dati 's il weak_ptr senza ricontrollare. Ingenuamente, potrebbe essere evitato con eccezioni, ma dopo un esame più approfondito si incontrano problemi in cui è necessario codificare tutti i metodi per verificare l'eliminazione prima di accedere a this e lanciare se si elimina this, un requisito piuttosto duro!)

Problemi correlati