Sposób myślenia na ten temat to „myśleć jak kompilator”.
Wyobraź sobie, że piszesz kompilator. I widzisz taki kod.
// 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;
}
Podczas kompilowania pliku .cc (pamiętaj, że .cc, a nie .h jest jednostką kompilacji), musisz przydzielić miejsce dla obiektu A
. A więc ile miejsca? Wystarczy do przechowywania B
! Jaki jest wtedy rozmiar B
? Wystarczająco do przechowywaniaA
! Ups
Wyraźnie okrągłe odniesienie, które musisz złamać.
Możesz go złamać, pozwalając kompilatorowi zamiast tego zarezerwować tyle miejsca, ile wie na temat początkowych - na przykład wskaźniki i referencje zawsze będą miały 32 lub 64 bity (w zależności od architektury), więc jeśli zastąpisz (jedno) przez wskaźnik lub odniesienie, byłoby świetnie. Powiedzmy, że zastępujemy A
:
// file: A.h
class A {
// both these are fine, so are various const versions of the same.
B& _b_ref;
B* _b_ptr;
};
Teraz jest lepiej. Nieco. main()
wciąż mówi:
// file: main.cc
#include "A.h" // <-- Houston, we have a problem
#include
, dla wszystkich zakresów i celów (jeśli wyjmiesz preprocesor), po prostu skopiuj plik do .cc . Tak naprawdę .cc wygląda następująco:
// file: partially_pre_processed_main.cc
class A {
B& _b_ref;
B* _b_ptr;
};
#include "B.h"
int main (...) {
A a;
}
Możesz zobaczyć, dlaczego kompilator nie może sobie z tym poradzić - nie ma pojęcia co B
jest - nigdy wcześniej nie widział tego symbolu.
Powiedzmy więc kompilatorowi B
. Jest to znane jako deklaracja forward i jest omówione w dalszej części tej odpowiedzi .
// main.cc
class B;
#include "A.h"
#include "B.h"
int main (...) {
A a;
}
To działa . To nie jest świetne . Ale w tym momencie powinieneś zrozumieć problem z okrągłym odniesieniem i to, co zrobiliśmy, aby go „naprawić”, chociaż naprawa jest zła.
Przyczyną tego, że ta poprawka jest zła, jest to, że następna osoba #include "A.h"
musi zadeklarować, B
zanim będzie mogła z niej skorzystać i otrzyma straszny #include
błąd. Przejdźmy więc do deklaracji samego Ah .
// file: A.h
class B;
class A {
B* _b; // or any of the other variants.
};
A w Bh , w tym momencie możesz po prostu#include "A.h"
bezpośrednio.
// 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.