Способ думать об этом - «думать как компилятор».
Представьте, что вы пишете компилятор. И вы видите такой код.
// 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-битными (в зависимости от архитектуры), и поэтому, если вы заменили (либо 1) по указателю или по ссылке все было бы замечательно. Допустим, мы заменим в 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
. Итак, давайте перенесем объявление в A.h .
// file: A.h
class B;
class A {
B* _b; // or any of the other variants.
};
И в B.h , на данный момент, вы можете просто #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;
}
НТН.