I often find myself in a situation where I am facing multiple compilation/linker errors in a C++ project due to some bad design decisions (made by someone else :) ) which lead to circular dependencies between C++ classes in different header files (can happen also in the same file). But fortunately(?) this doesn't happen often enough for me to remember the solution to this problem for the next time it happens again.
So for the purposes of easy recall in the future I am going to post a representative problem and a solution along with it. Better solutions are of-course welcome.
A.h
class B; class A { int _val; B *_b; public: A(int val) :_val(val) { } void SetB(B *b) { _b = b; _b->Print(); // COMPILER ERROR: C2027: use of undefined type 'B' } void Print() { cout<<"Type:A val="<<_val<<endl; } };
B.h
#include "A.h" class B { double _val; A* _a; public: B(double val) :_val(val) { } void SetA(A *a) { _a = a; _a->Print(); } void Print() { cout<<"Type:B val="<<_val<<endl; } };
main.cpp
#include "B.h" #include <iostream> int main(int argc, char* argv[]) { A a(10); B b(3.14); a.Print(); a.SetB(&b); b.Print(); b.SetA(&a); return 0; }
You can avoid compilation errors if you remove the method definitions from the header files and let the classes contain only the method declarations and variable declarations/definitions. The method definitions should be placed in a .cpp file (just like a best practice guideline says).
The down side of the following solution is (assuming that you had placed the methods in the header file to inline them) that the methods are no longer inlined by the compiler and trying to use the inline keyword produces linker errors.
The way to think about this is to "think like a compiler".
Imagine you are writing a compiler. And you see code like this.
When you are compiling the .cc file (remember that the .cc and not the .h is the unit of compilation), you need to allocate space for object
A
. So, well, how much space then? Enough to storeB
! What's the size ofB
then? Enough to storeA
! Oops.Clearly a circular reference that you must break.
You can break it by allowing the compiler to instead reserve as much space as it knows about upfront - pointers and references, for example, will always be 32 or 64 bits (depending on the architecture) and so if you replaced (either one) by a pointer or reference, things would be great. Let's say we replace in
A
:Now things are better. Somewhat.
main()
still says:#include
, for all extents and purposes (if you take the preprocessor out) just copies the file into the .cc. So really, the .cc looks like:You can see why the compiler can't deal with this - it has no idea what
B
is - it has never even seen the symbol before.So let's tell the compiler about
B
. This is known as a forward declaration, and is discussed further in this answer.This works. It is not great. But at this point you should have an understanding of the circular reference problem and what we did to "fix" it, albeit the fix is bad.
The reason this fix is bad is because the next person to
#include "A.h"
will have to declareB
before they can use it and will get a terrible#include
error. So let's move the declaration into A.h itself.And in B.h, at this point, you can just
#include "A.h"
directly.HTH.
Here is the solution for templates: How to handle circular dependencies with templates
The clue to solving this problem is to declare both classes before providing the definitions (implementations). It’s not possible to split the declaration and definition into separate files, but you can structure them as if they were in separate files.
The simple example presented on Wikipedia worked for me. (you can read the complete description at http://en.wikipedia.org/wiki/Circular_dependency#Example_of_circular_dependencies_in_C.2B.2B )
File '''a.h''':
File '''b.h''':
File '''main.cpp''':
Things to remember:
class A
has an object ofclass B
as a member or vice versa.Read the FAQ:
I've written a post about this once: Resolving circular dependencies in c++
The basic technique is to decouple the classes using interfaces. So in your case: