“Tipo incompleto” in class che ha un membro dello stesso tipo della class stessa

Ho una class che dovrebbe avere un membro privato della stessa class, qualcosa come:

class A { private: A member; } 

Ma mi dice che il membro è un tipo incompleto. Perché? Non mi dice tipo incompleto se uso un puntatore, ma preferirei non usare un puntatore. Qualsiasi aiuto è apprezzato

Nel momento in cui dichiari il tuo membro, stai ancora definendo la class A , quindi il tipo A è ancora indefinito.

Tuttavia, quando scrivi A* , il compilatore sa già che A sta per un nome di class, e quindi il tipo “puntatore ad A” è definito . Ecco perché puoi incorporare un puntatore al tipo che stai definendo.

La stessa logica vale anche per altri tipi, quindi se scrivi semplicemente:

 class Foo; 

Dichiari la class Foo, ma non la definisci mai. Tu puoi scrivere:

 Foo* foo; 

Ma no:

 Foo foo; 

D’altra parte, quale struttura di memoria ti aspetteresti per il tuo tipo A se il compilatore consentisse una definizione ricorsiva?

Tuttavia, a volte è logicamente valido avere un tipo che in qualche modo si riferisce a un’altra istanza dello stesso tipo. Le persone solitamente usano i puntatori per questo o anche meglio: puntatori intelligenti (come boost::shared_ptr ) per evitare di dover gestire l’eliminazione manuale.

Qualcosa di simile a:

 class A { private: boost::shared_ptr member; }; 

Questo è un esempio funzionante di ciò che stai cercando di ottenere:

 class A { public: A() : a(new A()) {} ~A() { delete a; a = nullptr; } private: A* a; }; A a; 

Happy Stack Overflow!

A è “incompleto” fino alla fine della sua definizione (anche se questo non include i corpi delle funzioni membro).

Uno dei motivi di ciò è che, fino alla fine della definizione, non c’è modo di sapere quanto è grande A (che dipende dalla sum delle dimensioni dei membri, più alcune altre cose). Il tuo codice è un ottimo esempio di ciò: il tuo tipo A è definito dalla dimensione del tipo A

Chiaramente, un object di tipo A non può contenere un object membro che è anche di tipo A

Dovrai memorizzare un puntatore o un riferimento; volere immagazzinare è forse sospetto.

Non puoi includere A all’interno di A. Se sei stato in grado di farlo, e hai dichiarato, ad esempio, A a; , dovresti fare riferimento a un a.member.member.member... di a.member.member.member... infinitamente. Non hai molta RAM disponibile.

Come può un’istanza di class A contenere anche un’altra istanza di class A ?

Può tenere un puntatore ad A se vuoi.

Questo tipo di errore si verifica quando si tenta di utilizzare una class che non è stata ancora completamente DEFINITA.

Prova ad usare invece A* member .

Il problema si verifica quando il compilatore incontra un object di codice A. Il compilatore si strofina la mano e inizia a fare un object di A. Mentre lo fa vedrà che A ha un membro che è di nuovo di tipo A. Quindi per completare l’istanziazione di A ora deve istanziare un altro A, e in così facendo deve istanziare un altro A e così via. Puoi vederlo finire in una ricorsione senza limiti. Quindi questo non è permesso. Il compilatore si assicura che conosca tutti i tipi e requisiti di memoria di tutti i membri prima che inizi a creare un’istanza di un object di una class.

Un modo semplice per comprendere il motivo dietro il quale la class A è incompleta è provare a guardarlo dal punto di vista del compilatore.

Tra le altre cose, il compilatore deve essere in grado di calcolare la dimensione di A object. Conoscere le dimensioni è un requisito fondamentale che si manifesta in molti contesti, come allocare spazio nella memoria automatica, chiamare l’operatore new e valutare la dimensione sizeof(A) . Tuttavia, calcolare la dimensione di A richiede conoscere la dimensione di A , perché a è un membro di A Questo porta alla ricorsione infinita.

Il modo in cui il compilatore affronta questo problema è considerare A incompleto finché la sua definizione non è completamente nota. Sei autorizzato a dichiarare puntatori e riferimenti a classi incomplete, ma non sei autorizzato a dichiarare valori.