Спосіб подумати над цим - "думати, як компілятор".
Уявіть, що ви пишете компілятор. І ви бачите такий код.
// 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;
}
Коли ви збираєте файл .cc (пам’ятайте, що .cc, а не .h є одиницею компіляції), вам потрібно виділити простір для об’єкта A
. Отже, ну скільки місця тоді? Достатньо для зберігання B
! Який розмір B
тоді? Досить зберігатиA
! На жаль
Очевидно кругла посилання, яку ви повинні перервати.
Ви можете зламати це, дозволяючи компілятору замість цього зарезервувати стільки місця, скільки він знає про наперед - покажчики та посилання, наприклад, завжди будуть 32 або 64 біти (залежно від архітектури), і якщо ви замінили (будь-якого) на вказівник чи посилання, все було б чудово. Скажімо, ми замінюємо A
:
// file: A.h
class A {
// both these are fine, so are various const versions of the same.
B& _b_ref;
B* _b_ptr;
};
Тепер все краще. Дещо. main()
все ще каже:
// file: main.cc
#include "A.h" // <-- Houston, we have a problem
#include
, для всіх розширень та цілей (якщо ви вийдете з препроцесора) просто скопіюйте файл у .cc . Так насправді, .cc виглядає так:
// file: partially_pre_processed_main.cc
class A {
B& _b_ref;
B* _b_ptr;
};
#include "B.h"
int main (...) {
A a;
}
Ви можете зрозуміти, чому компілятор не може з цим впоратися - він поняття не має, що B
таке - він ніколи навіть не бачив символу раніше.
Тож давайте розповімо про компілятора B
. Це відоме як пряма декларація , і це буде обговорено далі в цій відповіді .
// main.cc
class B;
#include "A.h"
#include "B.h"
int main (...) {
A a;
}
Це працює . Це не велико . Але в цей момент у вас повинно бути розуміння кругової посилальної проблеми та того, що ми зробили, щоб "виправити" її, хоча це виправлення є поганим.
Причина цього виправлення погана в тому, що наступна особа повинна #include "A.h"
буде заявити, B
перш ніж вони зможуть її використати, і отримає жахливу #include
помилку. Тож давайте перейдемо до декларації саму Ах .
// file: A.h
class B;
class A {
B* _b; // or any of the other variants.
};
І в Bh , на даний момент, можна просто#include "A.h"
безпосередньо.
// 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.