unique_ptr<T, D>
è in realtà progettato appositamente per essere in grado di lavorare con tipi di handle più arbitrari. Ho spiegato completamente il nome del modello perché D
è la chiave qui. Normalmente unique_ptr<T, D>::get()
restituisce un T*
. Questa è l'impostazione predefinita, ma può essere sostituita da D
: il tipo deleter.
Se il tipo deleter ha un alias pointer
( D::pointer
è una sintassi legale), quindi unique_ptr<T, D>::get()
restituirà quel tipo. Questo ti permette di avere qualcosa come unique_ptr<GLuint, gl::program_deleter>
, dove gl::program_deleter::pointer
è di tipo int
.
Porto tutto questo perché shared_ptr
non può farlo . unique_ptr<T, D>
viene eliminato perché il deleter è in realtà parte del tipo unique_ptr
stesso. Al contrario, mentre i costruttori di shared_ptr
possono prendere un deleter, l'unica cosa che la funzione deleter può fare è cancellare la memoria.
Quindi shared_ptr<GLuint>::get()
restituirà sempre GLuint*
. Ciò significa che, se si desidera utilizzare shared_ptr
come una sorta di tipo di handle condiviso, quel tipo deve essere assegnato in modo dinamico in qualche modo. È possibile che non si stia utilizzando l'heap globale, ma non è possibile archiviare e restituire l'intero. shared_ptr<T>
contiene sempre T*
.
Quindi, non importa cosa, dovrai gestire GLuint*
s se vuoi usare il meccanismo di conteggio dei riferimenti di shared_ptr
. Sì, il deleter può essere utilizzato per chiamare glDeleteProgram
o qualsiasi cosa tu desideri, ma shared_ptr<GLuint>
continuerà a memorizzare GLuint*
.
creating an OpenGL shader program handle does not actually involve heap memory
OK, dimentichiamo per un momento che, creando un oggetto OpenGL, il driver ha quasi certamente accumulato un po 'di memoria. Diamo un'occhiata a ciò che devi fare.
Creando un shared_ptr
a cui appartiene un certo spazio di archiviazione, verrà assegnato un qualcosa . Vale a dire, il blocco condiviso che gestisce il conteggio dei riferimenti di shared_ptr
. Non c'è modo di aggirare questo. Pertanto, se desideri utilizzare l'infrastruttura di conteggio di riferimento di shared_ptr
, dovrai allocare da qualche parte.
Quindi il modo più idiomatico per farlo è quello di ammucchiare solo allocare un GLuint
e usare un deleter speciale che distrugge l'oggetto OpenGL e rilascia il numero intero. Non è carino ed è un po 'dispendioso, ma non è affatto terribile. E se utilizzi make_shared
, puoi rendere le cose abbastanza compatte in termini di allocazioni.
Ora puoi evitare questa allocazione con imbrogli . Puoi fare ciò:
GLuint program = glCreateProgram();
shared_ptr<GLuint> sp(reinterpret_cast<GLuint*>(program), ProgramDeleter);
Quindi, qui prendiamo un numero intero e lo gettiamo su un valore puntatore, per essere memorizzato all'interno di shared_ptr
. Quando hai bisogno di usarlo, devi invertire il cast per recuperare il valore intero.
Ma giudica il seguente codice:
glProgramUniform1i(reinterpret_cast<GLuint>(sp.get()), val);
Sembra che tu voglia fare qualcosa di frequente? Sembra che tu voglia leggere di frequente? Sembra qualcosa che qualcun altro capirà facilmente cosa sta succedendo?
Non solo, non puoi mai usare *sp
per ottenere il valore, poiché il valore del puntatore è il valore in questione.
Oh, e il blocco di controllo del conteggio dei riferimenti viene ancora assegnato all'heap, quindi non è come se impedissi di allocare memoria o qualcosa del genere.
Questo non è un C ++ idiomatico.