0. Keywords
Cyclic Dependency, Circular Dependency, Cross Dependency, Forward Declaration.
1. Cyclic Dependency (Circular Dependency, Cross Dependency) Problem
Two classes depending on each other would cause Cyclic Dependency (Circular Dependency, Cross Dependency). For example, class A (in A.h) is associated with class B in (B.h) and vice versa.
main.cpp |
int main() {return 0;} |
A.h |
#ifndef _A_H_ #include "B.h" class A{ #endif |
B.h |
#ifndef _B_H_ #include "A.h" class B{ }; #endif |
Let's see what would happen by tracing the code at main.cpp.
At frist, the statement #include "A.h" is placed. Preprocessor go to A.h, finding out that _A_H_ is not yet defined (#ifndef _A_H_), so preprocessor define _A_H_ (#define _A_H_ ). After that, it encounter #include "B.h".
Then preprocessor go to B.h, finding out that _B_H_ is not yet defined (#ifndef _B_H_), so preprocessor define _B_H_ (#define _B_H_ ). After that, it encounter #include "A.h". However, A.h will not be included in B.h again because _A_H_ is already defined now.
Note that class B have no idea about A. Therefore, in the the declararion of class B in B.H, declararing A* a; will cause error since A is not a class, a struct, or a primitive type in the view of class B. The error message would be something like:
error C4430: miss type - C++ does not support default-int.
2. solution
The Circular Dependency Problem can be solved by Forward Declaration of class A such that class B would know A is a class.
B.h |
#ifndef _B_H_ #include "A.h" class A; class B{ #endif |
3. Incomplete Type Problem for Forward Declaration
Before a type's definition is seen, this type is called incomplete type. The statement
class A; in B.h in the code above tells that A is a class without definition. In this case, A is an incomplete type.
It is not possible use an incomplete type to define a variable or class member. An incomplete type may be used to define only pointers or references to the type or to declare (but not define) functions that use the type as a paremeter or return type.[1]
In the following code, we observe that the statement a->doSomething(); in B.h causes a error because class B knows A is a class only and the definition of doSomething(); of class A has not yet been seen.
Moreover, after we delete a->doSomething(); in B.h, no error messages would show. But why b->doSomething(); in A.h works fine? It because the class A know everything about class B, including the definition of B. The class B is a complete type in the view of class A.
main.cpp |
#include "A.h" int main() {return 0;} |
A.h |
#ifndef _A_H_ #include "B.h" class B; class A{ }; #endif |
B.h |
#ifndef _B_H_ #include "A.h" class A; class B{ #endif |
Similarly, we know the fact that an incomplete type can not define a variable or class member. Thus ,the following code causes a error when defining an incomplete class member (The statement A a; in class B)
A.h |
#ifndef _A_H_ #include "B.h" class B; class A{ #endif |
B.h |
#ifndef _B_H_ #include "A.h" class A; class B{ #endif |
Reference
[1] By Stanley B. Lippman, Josée Lajoie, Barbara E. Moo, "C++ Primer, Fourth Edition", Publisher: Addison Wesley Professional, Pub Date: February 14, 2005, ISBN: 0-201-72148-1.
[2] Forward declaration From Wikipedia.
[3] Circular dependency From Wikipedia.