403

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;
    }
    
StoryTeller - Unslander Monica
  • 148,497
  • 21
  • 320
  • 399
Autodidact
  • 25,926
  • 15
  • 63
  • 81
  • 23
    When working with Visual Studio, the [/showIncludes](https://msdn.microsoft.com/en-us/library/hdkef6tk.aspx) flag helps a lot to debug this kind of problems. – wip Sep 12 '12 at 03:08

11 Answers11

327

The way to think about this is to "think like a compiler".

Imagine you are writing a compiler. And you see code like this.

// 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;
}

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 store B! What's the size of B then? Enough to store A! 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:

// file: A.h
class A {
  // both these are fine, so are various const versions of the same.
  B& _b_ref;
  B* _b_ptr;
};

Now things are better. Somewhat. main() still says:

// file: main.cc
#include "A.h"  // <-- Houston, we have a problem

#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:

// file: partially_pre_processed_main.cc
class A {
  B& _b_ref;
  B* _b_ptr;
};
#include "B.h"
int main (...) {
  A a;
}

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.

// main.cc
class B;
#include "A.h"
#include "B.h"
int main (...) {
  A a;
}

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 declare B before they can use it and will get a terrible #include error. So let's move the declaration into A.h itself.

// file: A.h
class B;
class A {
  B* _b; // or any of the other variants.
};

And in B.h, at this point, you can just #include "A.h" directly.

// 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.

Community
  • 1
  • 1
Roosh
  • 3,354
  • 2
  • 14
  • 2
  • 22
    "Telling the compiler about B" is known as a forward declaration of B. – Peter Ajtai Nov 17 '10 at 01:57
  • 10
    Omg! totally missed the fact that references are known in terms of occupied space. Finally, now I can design properly! – kellogs Nov 07 '11 at 02:31
  • 58
    But still You cannot use any function on B (as in the question _b->Printt()) – rank1 Apr 17 '13 at 11:02
  • 3
    This is the issue I'm having. How do you bring the functions in with forward declaration without completely rewriting the header file? – sydan Feb 03 '15 at 13:56
  • 4
    @sydan: You can't. [Resolving circular dependencies *requires* out-of-class definitions](http://stackoverflow.com/q/7714345/103167). – Ben Voigt Apr 11 '15 at 14:03
  • 3
    But I need to use in `A` class B as a complete type and in `B` class A as a complete type. By saying complete type, I mean, calling a function from an object of that type. How would I do it? I just get error, `invalid use of incomplete type B in class A`. – Silidrone Sep 05 '17 at 12:47
  • Why compiler not take steps around circular dependency sizeofA = sizeof(A) - reference to b : sizeofB = sizeof(B) - reference to a : realSzA = realSzB = ( sizeofA + sizeOfB ) – Eladian Apr 17 '18 at 11:48
  • 1
    @rank1: Sandeep Datta's answer below solves this problem. Forward declare the classes in the header files, and include both headers in both .cpp files. (That is 2 forward declarations and 4 includes.) – MindSeeker Jul 16 '18 at 21:26
  • @MindSeeker Forward declare class B in "A.h" -> include "A.h" in "B.h" and then include only "B.h" in both "A.cpp" and "B.cpp". (That is 1 forward declarationand and 3 includes) – Gusev Slava Sep 30 '18 at 04:37
  • Note that it’s unspecified whether reference members actually require storage at all—since you can’t take their address, there’s no legitimate way to tell. – Davis Herring Dec 01 '19 at 18:55
112

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.

//A.h
#ifndef A_H
#define A_H
class B;
class A
{
    int _val;
    B* _b;
public:

    A(int val);
    void SetB(B *b);
    void Print();
};
#endif

//B.h
#ifndef B_H
#define B_H
class A;
class B
{
    double _val;
    A* _a;
public:

    B(double val);
    void SetA(A *a);
    void Print();
};
#endif

//A.cpp
#include "A.h"
#include "B.h"

#include <iostream>

using namespace std;

A::A(int val)
:_val(val)
{
}

void A::SetB(B *b)
{
    _b = b;
    cout<<"Inside SetB()"<<endl;
    _b->Print();
}

void A::Print()
{
    cout<<"Type:A val="<<_val<<endl;
}

//B.cpp
#include "B.h"
#include "A.h"
#include <iostream>

using namespace std;

B::B(double val)
:_val(val)
{
}

void B::SetA(A *a)
{
    _a = a;
    cout<<"Inside SetA()"<<endl;
    _a->Print();
}

void B::Print()
{
    cout<<"Type:B val="<<_val<<endl;
}

//main.cpp
#include "A.h"
#include "B.h"

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;
}
Autodidact
  • 25,926
  • 15
  • 63
  • 81
  • Thanks. This solved the problem easily. I simply moved the circular includes to the .cpp files. – Lenar Hoyt Oct 04 '14 at 18:16
  • 4
    What if you have a template method? Then you can't really move it into a CPP file unless you instantiate the templates manually. – Malcolm Sep 01 '16 at 12:55
  • You always include "A.h" and "B.h" together. Why don't you include "A.h" in "B.h" and then include only "B.h" in both "A.cpp" and "B.cpp"? – Gusev Slava Sep 30 '18 at 04:25
  • Thanks, Nice answer for those who need this interdependence between 2 classes and cannot refactor it differently – HanniBaL90 Dec 22 '20 at 14:34
39

I'm late answering this, but there's not one reasonable answer to date, despite being a popular question with highly upvoted answers....

Best practice: forward declaration headers

As illustrated by the Standard library's <iosfwd> header, the proper way to provide forward declarations for others is to have a forward declaration header. For example:

a.fwd.h:

#pragma once
class A;

a.h:

#pragma once
#include "a.fwd.h"
#include "b.fwd.h"

class A
{
  public:
    void f(B*);
};

b.fwd.h:

#pragma once
class B;

b.h:

#pragma once
#include "b.fwd.h"
#include "a.fwd.h"

class B
{
  public:
    void f(A*);
};

The maintainers of the A and B libraries should each be responsible for keeping their forward declaration headers in sync with their headers and implementation files, so - for example - if the maintainer of "B" comes along and rewrites the code to be...

b.fwd.h:

template <typename T> class Basic_B;
typedef Basic_B<char> B;

b.h:

template <typename T>
class Basic_B
{
    ...class definition...
};
typedef Basic_B<char> B;

...then recompilation of the code for "A" will be triggered by the changes to the included b.fwd.h and should complete cleanly.


Poor but common practice: forward declare stuff in other libs

Say - instead of using a forward declaration header as explained above - code in a.h or a.cc instead forward-declares class B; itself:

  • if a.h or a.cc did include b.h later:
    • compilation of A will terminate with an error once it gets to the conflicting declaration/definition of B (i.e. the above change to B broke A and any other clients abusing forward declarations, instead of working transparently).
  • otherwise (if A didn't eventually include b.h - possible if A just stores/passes around Bs by pointer and/or reference)
    • build tools relying on #include analysis and changed file timestamps won't rebuild A (and its further-dependent code) after the change to B, causing errors at link time or run time. If B is distributed as a runtime loaded DLL, code in "A" may fail to find the differently-mangled symbols at runtime, which may or may not be handled well enough to trigger orderly shutdown or acceptably reduced functionality.

If A's code has template specialisations / "traits" for the old B, they won't take effect.

Tony Delroy
  • 94,554
  • 11
  • 158
  • 229
  • 2
    This is a really clean way to handle the forward declarations. The only _"disadvantage"_ would be in the extra files. I assume you always include `a.fwd.h` in `a.h`, to assure they stay in sync. The example code is missing where these classes are used. `a.h` and `b.h` will both need to be included since they won't function in isolation: ``` //main.cpp #include "a.h" #include "b.h" int main() { ... } ``` Or one of them needs to be fully included in the other like in the opening question. Where `b.h` includes `a.h` and `main.cpp` includes `b.h` – Farway May 05 '17 at 16:37
  • 2
    @Farway Right on all counts. I didn't bother showing `main.cpp`, but nice that you've documented what it should contain in your comment. Cheers – Tony Delroy May 05 '17 at 20:42
  • 1
    One of the better answers with a nice detailed explanation of why with the does and don'ts due to the pros and cons... – Francis Cugler Jan 16 '18 at 05:06
  • According to this solution, do we need to have a separate Class.fwd.h file for every class we have? Or just provide forward declaration of a class where it's suspected of having circular dependency? – Reza Hajianpour Jan 22 '19 at 21:12
  • 1
    @RezaHajianpour: it makes sense to have a forward declaration header for all classes that you want forward declarations of, circular or not. That said, you will only want them when: 1) including the actual declaration is (or can be anticipated to later become) costly (e.g. it includes a lot of headers your translation unit might not otherwise need), and 2) client code is likely to be able to make use of pointers or references to the objects. `` is a classic example: there can be a few stream objects referenced from many places, and `` is a lot to include. – Tony Delroy Jan 23 '19 at 05:02
  • @TonyDelroy Therefore, we can include the forward declaration headers only, if we just need the type to be defined and reside in scope, without including more headers, right? – Reza Hajianpour Jan 25 '19 at 00:41
  • 1
    @RezaHajianpour: I think you have the right idea, but there's a terminological issue with your statement: "we just need the type to be ***declared***" would be right. The type being *declared* means the forward declaration has been seen; it's *defined* once the full definition has been parsed (and for that you *may* need more `#include`s). – Tony Delroy Jan 25 '19 at 14:45
  • 1
    I hope this answer gets seen more and upvoted enough to be front and centre. as with an over 230 point gap between this and the top answer, this isn't going to get a look in. – Force Gaia Jun 18 '19 at 16:08
22

Things to remember:

  • This won't work if class A has an object of class B as a member or vice versa.
  • Forward declaration is way to go.
  • Order of declaration matters (which is why you are moving out the definitions).
    • If both classes call functions of the other, you have to move the definitions out.

Read the FAQ:

Ken Y-N
  • 12,690
  • 21
  • 62
  • 98
dirkgently
  • 101,474
  • 16
  • 123
  • 183
14

I once solved this kind of problem by moving all inlines after the class definition and putting the #include for the other classes just before the inlines in the header file. This way one make sure all definitions+inlines are set prior the inlines are parsed.

Doing like this makes it possible to still have a bunch of inlines in both(or multiple) header files. But it's necessary to have include guards.

Like this

// File: A.h
#ifndef __A_H__
#define __A_H__
class B;
class A
{
    int _val;
    B *_b;
public:
    A(int val);
    void SetB(B *b);
    void Print();
};

// Including class B for inline usage here 
#include "B.h"

inline A::A(int val) : _val(val)
{
}

inline void A::SetB(B *b)
{
    _b = b;
    _b->Print();
}

inline void A::Print()
{
    cout<<"Type:A val="<<_val<<endl;
}

#endif /* __A_H__ */

...and doing the same in B.h

epatel
  • 44,965
  • 17
  • 105
  • 143
  • Why? I think it's an elegant solution to a tricky problem...when one wants inlines. If one don't want inlines one shouldn't have written the code like it was written from start... – epatel Mar 10 '09 at 20:01
  • What happens if a user includes `B.h` first? – Mr Fooz Mar 18 '14 at 16:00
  • 3
    Note that your header guard is using a reserved identifier, anything with double adjacent underscores is reserved. – Lars Viklund Aug 10 '15 at 15:09
7

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:

//Printer.h
class Printer {
public:
    virtual Print() = 0;
}

//A.h
#include "Printer.h"
class A: public Printer
{
    int _val;
    Printer *_b;
public:

    A(int val)
        :_val(val)
    {
    }

    void SetB(Printer *b)
    {
        _b = b;
        _b->Print();
    }

    void Print()
    {
        cout<<"Type:A val="<<_val<<endl;
    }
};

//B.h
#include "Printer.h"
class B: public Printer
{
    double _val;
    Printer* _a;
public:

    B(double val)
        :_val(val)
    {
    }

    void SetA(Printer *a)
    {
        _a = a;
        _a->Print();
    }

    void Print()
    {
        cout<<"Type:B val="<<_val<<endl;
    }
};

//main.cpp
#include <iostream>
#include "A.h"
#include "B.h"

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;
}
Eduard Wirch
  • 9,361
  • 9
  • 58
  • 70
6

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.

Tatyana
  • 81
  • 1
  • 4
4

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''':

#ifndef A_H
#define A_H

class B;    //forward declaration

class A {
public:
    B* b;
};
#endif //A_H

File '''b.h''':

#ifndef B_H
#define B_H

class A;    //forward declaration

class B {
public:
    A* a;
};
#endif //B_H

File '''main.cpp''':

#include "a.h"
#include "b.h"

int main() {
    A a;
    B b;
    a.b = &b;
    b.a = &a;
}
madx
  • 5,688
  • 4
  • 47
  • 53
2

Unfortunately, all the previous answers are missing some details. The correct solution is a little bit cumbersome, but this is the only way to do it properly. And it scales easily, handles more complex dependencies as well.

Here's how you can do this, exactly retaining all the details, and usability:

  • the solution is exactly the same as originally intended
  • inline functions still inline
  • users of A and B can include A.h and B.h in any order

Create two files, A_def.h, B_def.h. These will contain only A's and B's definition:

// A_def.h
#ifndef A_DEF_H
#define A_DEF_H

class B;
class A
{
    int _val;
    B *_b;

public:
    A(int val);
    void SetB(B *b);
    void Print();
};
#endif

// B_def.h
#ifndef B_DEF_H
#define B_DEF_H

class A;
class B
{
    double _val;
    A* _a;

public:
    B(double val);
    void SetA(A *a);
    void Print();
};
#endif

And then, A.h and B.h will contain this:

// A.h
#ifndef A_H
#define A_H

#include "A_def.h"
#include "B_def.h"

inline A::A(int val) :_val(val)
{
}

inline void A::SetB(B *b)
{
    _b = b;
    _b->Print();
}

inline void A::Print()
{
    cout<<"Type:A val="<<_val<<endl;
}

#endif

// B.h
#ifndef B_H
#define B_H

#include "A_def.h"
#include "B_def.h"

inline B::B(double val) :_val(val)
{
}

inline void B::SetA(A *a)
{
    _a = a;
    _a->Print();
}

inline void B::Print()
{
    cout<<"Type:B val="<<_val<<endl;
}

#endif

Note that A_def.h and B_def.h are "private" headers, users of A and B should not use them. The public header is A.h and B.h.

geza
  • 26,117
  • 6
  • 47
  • 111
  • 1
    Does this have any advantages over [Tony Delroy's solution](https://stackoverflow.com/a/29209540/3982001)? Both are based on "helper" headers, but Tony's are smaller (they just contain the forward declaration) and they seem to be working the same way (at least at first glance). – Fabio says Reinstate Monica Nov 20 '18 at 11:33
  • 1
    That answer doesn't solve the original problem. It just says "put forward declarations into a separate header". Nothing about resolving circular dependency (the question needs a solution where `A`'s and `B`'s definition is available, forward declaration is not enough). – geza Nov 20 '18 at 11:50
0

In some cases it is possible to define a method or a constructor of class B in the header file of class A to resolve circular dependencies involving definitions. In this way you can avoid having to put definitions in .cc files, for example if you want to implement a header only library.

// file: a.h
#include "b.h"
struct A {
  A(const B& b) : _b(b) { }
  B get() { return _b; }
  B _b;
};

// note that the get method of class B is defined in a.h
A B::get() {
  return A(*this);
}

// file: b.h
class A;
struct B {
  // here the get method is only declared
  A get();
};

// file: main.cc
#include "a.h"
int main(...) {
  B b;
  A a = b.get();
}

jkoendev
  • 35
  • 6
0

Unfortunately I can't comment the answer from geza.

He is not just saying "put forward declarations into a separate header". He says that you have to spilt class definition headers and inline function definitions into different header files to allow "defered dependencies".

But his illustration is not really good. Because both classes (A and B) only need an incomplete type of each other (pointer fields / parameters).

To understand it better imagine that class A has a field of type B not B*. In addition class A and B want to define an inline function with parameters of the other type:

This simple code would not work:

// A.h
#pragme once
#include "B.h"

class A{
  B b;
  inline void Do(B b);
}

inline void A::Do(B b){
  //do something with B
}

// B.h
#pragme once
class A;

class B{
  A* b;
  inline void Do(A a);
}

#include "A.h"

inline void B::Do(A a){
  //do something with A
}

//main.cpp
#include "A.h"
#include "B.h"

It would result in the following code:

//main.cpp
//#include "A.h"

class A;

class B{
  A* b;
  inline void Do(A a);
}

inline void B::Do(A a){
  //do something with A
}

class A{
  B b;
  inline void Do(B b);
}

inline void A::Do(B b){
  //do something with B
}
//#include "B.h"

This code does not compile because B::Do needs a complete type of A which is defined later.

To make sure that it compiles the source code should look like this:

//main.cpp
class A;

class B{
  A* b;
  inline void Do(A a);
}

class A{
  B b;
  inline void Do(B b);
}

inline void B::Do(A a){
  //do something with A
}

inline void A::Do(B b){
  //do something with B
}

This is exactly possible with these two header files for each class wich needs to define inline functions. The only issue is that the circular classes can't just include the "public header".

To solve this issue I would like to suggest a preprocessor extension: #pragma process_pending_includes

This directive should defer the processing of the current file and complete all pending includes.

Bernd
  • 1,589
  • 6
  • 16