Circular Dependency

From Cppwiki

Jump to: navigation, search

In C++ there are many situations where two classes need some sort of reference to each other. This can present a unique problem that can sometimes be difficult to diagnose. If include guards are not used, the preprocesor can be stuck in an infite inclusion loop (unless it has some mechanism to prevent it, in which case it will report an error and terminate). Include guards don't prevent the problem, but can obscure it slightly.

Example of Problem

You have two classes, both of which require pointers to each other. The header files of the two classes are shown below. As you can see, each class uses include guards.

//a.hpp
#ifndef A_HPP
#define A_HPP

#include "b.hpp"

class A {
        B* b;
};

#endif
//b.hpp
#ifndef B_HPP
#define B_HPP

#include "a.hpp"

class B {
        A* a;
};

#endif

Now, let's say you include a.hpp in some file. The header file is preprocessed into the text below:

class B {
        A* a;
};

class A {
        B* b;
};

The problem here is the definition of B uses class A which to that point in the file has not been declared. In "b.hpp" we are including "a.hpp" however, the include guard (luckily) prevents 'a.hpp' from being included again. "b.hpp" never gets to see a definition of A, and thus it is trying to use an undeclared type. The compiler must complain, report the error, and terminate.

Example of Solution

//a.hpp
#ifndef A_HPP
#define A_HPP

class B;

class A {
        B* b;
};

#endif
//b.hpp
#ifndef B_HPP
#define B_HPP

class A;

class B {
        A* a;
};

#endif

This solves the problem as the compiler only needs a declaration of B in order to allocate a pointer type to B for the class A. The class declaration is sufficient in order to signal the type exists, and that a pointer to B is a valid type.