Classe di dimensioni variabili - C ++
-
06-07-2019 - |
Domanda
Ho visto una classe che è una classe definita in questo modo.
class StringChild : public StringBase
{
public:
//some non-virtual functions
static StringChild* CreateMe(int size);
private:
unsigned char iBuf[1];
};
La funzione factory statica ha la seguente implementazione.
return new(malloc(__builtin_offsetof(StringChild ,iBuf[size]))) StringChild();
Per quanto ho capito, questa funzione sta usando il posizionamento nuovo per estendere questa classe.
È sicuro solo perché c'è solo 1 membro ed è allocato nell'heap?
Soluzione
È un vecchio trucco in C usato per aggirare la non disponibilità di matrici di lunghezza variabile nella pianura C. Sì, funziona anche in C ++ purché si utilizzino costrutti di allocatore adeguati (come allocare un mucchio di memoria grezza dimensione desiderata e quindi posizionamento rinnovando l'oggetto). È sicuro fino a quando non vaghi per la fine della memoria allocata, ma tende a confondere almeno alcuni debugger di memoria.
Una cosa che devi assolutamente accertare quando usi questa tecnica è che l'array a lunghezza variabile è l'ultimo elemento nel layout dell'oggetto, altrimenti passerai sopra altre variabili interne.
Sono comunque un po 'dubbioso sull'implementazione della funzione factory - suppongo che il parametro' size 'sia effettivamente la dimensione dell'array desiderata? Inoltre, non dimenticare che dovresti rilasciare la memoria sopra usando 'free' e non 'delete', anche se quest'ultimo potrebbe funzionare nella maggior parte dei casi.
A meno che non ci sia una ragione convincente per cui la memoria deve essere gestita in questo modo, sostituirò semplicemente l'array con uno std :: vector.
Altri suggerimenti
Questo dovrebbe essere OK per i POD a condizione che iBuf sia l'ultimo membro della struttura. I problemi con i non POD potrebbero essere ad es. il compilatore è libero di riordinare membri pubblici / privati ??/ protetti, le classi di base virtuali finiscono alla fine dell'oggetto IIUC più derivato, ecc.
La tua struttura non è POD (ha una classe base) quindi non la consiglierei.
Inoltre, se crei istanze come questa
return new(malloc(__builtin_offsetof(StringChild ,iBuf[size]))) StringChild();
Dovresti assicurarti che la memoria acquisita da malloc debba essere liberata gratuitamente, quindi elimina le tue istanze in questo modo:
obj->~StringChild();
free(obj);
Forse ti piacerebbe usare :: operator new ()
per allocazione
A rigor di termini, poiché StringChild
è derivato da StringBase
non è sicuro. Lo standard C ++ non specifica il layout per gli oggetti secondari della classe base. Clausola 10, paragrafo 3:
L'ordine in cui gli oggetti secondari della classe base sono allocati nell'oggetto più derivato (1.8) non è specificato.
Se StringChild
fosse una struttura POD, una tale tecnica sarebbe sicura.