A maneira de pensar sobre isso é "pensar como um compilador".
Imagine que você está escrevendo um compilador. E você vê código assim.
// 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;
}
Ao compilar o arquivo .cc (lembre-se de que o .cc e não o .h é a unidade de compilação), você precisa alocar espaço para o objeto A
. Então, bem, quanto espaço então? O suficiente para guardar B
! Qual é o tamanho B
então? O suficiente para guardar A
! Opa
Claramente uma referência circular que você deve quebrar.
Você pode quebrá-lo, permitindo que o compilador reserve o máximo de espaço que sabe sobre o início - ponteiros e referências, por exemplo, sempre terão 32 ou 64 bits (dependendo da arquitetura) e, portanto, se você substituir (um) por um ponteiro ou referência, as coisas seriam ótimas. Digamos que substituímos em A
:
// file: A.h
class A {
// both these are fine, so are various const versions of the same.
B& _b_ref;
B* _b_ptr;
};
Agora as coisas estão melhores. Um pouco. main()
ainda diz:
// file: main.cc
#include "A.h" // <-- Houston, we have a problem
#include
, para todas as extensões e propósitos (se você retirar o pré-processador), basta copiar o arquivo no arquivo .cc . Então, realmente, o .cc se parece com:
// file: partially_pre_processed_main.cc
class A {
B& _b_ref;
B* _b_ptr;
};
#include "B.h"
int main (...) {
A a;
}
Você pode ver por que o compilador não pode lidar com isso - ele não tem idéia do que B
é - ele nunca viu o símbolo antes.
Então, vamos falar sobre o compilador B
. Isso é conhecido como declaração direta e é discutido mais adiante nesta resposta .
// main.cc
class B;
#include "A.h"
#include "B.h"
int main (...) {
A a;
}
Isso funciona . Isso não é ótimo . Mas, nesse ponto, você deve ter uma compreensão do problema de referência circular e o que fizemos para "corrigi-lo", embora a correção seja ruim.
O motivo dessa correção é ruim porque a próxima pessoa #include "A.h"
terá que declarar B
antes de poder usá-la e receberá um #include
erro terrível . Então, vamos passar a declaração para o próprio Ah .
// file: A.h
class B;
class A {
B* _b; // or any of the other variants.
};
E em Bh , neste ponto, você pode apenas #include "A.h"
diretamente.
// 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.