Il modo di pensarci è "pensare come un compilatore".
Immagina di scrivere un compilatore. E vedi un codice come questo.
// file: A.h
class A {
B _b;
};
// file: B.h
class B {
A _a;
};
// file main.cc
#include "A.h"
#include "B.h"
int main(...) {
A a;
}
Quando si compila il file .cc (ricordare che .cc e non .h è l'unità di compilazione), è necessario allocare spazio per l'oggetto A
. Allora, quanto spazio allora? Abbastanza da conservare B
! Qual è la dimensione di B
allora? Abbastanza da conservare A
! Ops.
Chiaramente un riferimento circolare che devi rompere.
Puoi romperlo consentendo al compilatore di riservare invece tutto lo spazio che sa in anticipo - puntatori e riferimenti, ad esempio, saranno sempre 32 o 64 bit (a seconda dell'architettura) e quindi se hai sostituito (uno dei due) con un puntatore o un riferimento, le cose sarebbero grandi. Diciamo che sostituiamo in A
:
// file: A.h
class A {
// both these are fine, so are various const versions of the same.
B& _b_ref;
B* _b_ptr;
};
Adesso le cose vanno meglio. Un po '. main()
dice ancora:
// file: main.cc
#include "A.h" // <-- Houston, we have a problem
#include
, a tutti gli effetti (se si estrae il preprocessore) copia semplicemente il file in .cc . Quindi, davvero, il .cc assomiglia a:
// file: partially_pre_processed_main.cc
class A {
B& _b_ref;
B* _b_ptr;
};
#include "B.h"
int main (...) {
A a;
}
Puoi capire perché il compilatore non può farcela - non ha idea di cosa B
sia - non ha mai visto il simbolo prima.
Quindi parliamo del compilatore B
. Questa è nota come dichiarazione a termine ed è discussa ulteriormente in questa risposta .
// main.cc
class B;
#include "A.h"
#include "B.h"
int main (...) {
A a;
}
Questo funziona . Non è fantastico . Ma a questo punto dovresti avere una comprensione del problema di riferimento circolare e di cosa abbiamo fatto per "risolverlo", sebbene la correzione sia sbagliata.
Il motivo per cui questa correzione è sbagliata è perché la prossima persona #include "A.h"
dovrà dichiarare B
prima di poterla usare e otterrà un terribile #include
errore. Quindi spostiamo la dichiarazione in Ah stessa.
// file: A.h
class B;
class A {
B* _b; // or any of the other variants.
};
E in Bh , a questo punto, puoi semplicemente #include "A.h"
direttamente.
// file: B.h
#include "A.h"
class B {
// note that this is cool because the compiler knows by this time
// how much space A will need.
A _a;
}
HTH.