Resolve Build Errors Due to Circular Dependency Amongst Classes

Resolve build errors due to circular dependency amongst classes

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.

Handling circular dependencies in C++ [duplicate]

Since the C++17 standard you don't need the full EntityBase class definition for the Scene class, only a forward declaration:

#pragma once
#include <vector>
#include <string>

class EntityBase; // Forward declaration

class Scene
{
public:
std::vector<EntityBase> entities;
std::string name;

Scene(std::string name);

void addToScene(EntityBase& entityBase);
};

You of course need to include EntityBase.h anywhere where the entities vector is used, most notable in the Scene.cpp source file.


Since EntityBase is using an actual instance of Scene you can't do the same with that class and the EntityBase.h header file. Here you must include the Scene.h header file.


If you build targeting an older C++ standard (C++14 or earlier) then you must have the full definition of the EntityBase class for the vector.

As a possible workaround either make entities a vector of pointers to EntityBase; Or make EntityBase::scene a pointer and use forward declaration in the EntityBase.h header file instead. Or a combination of both.

How to avoid circular dependency in this project? [duplicate]

If those are the only dependent functions, you could declare and initialize in the following order:

class Game;

class Enemy {
public:
void turn(Game* game);
};

class Game {
public:
std::vector<Enemy> enemies;
};

Live example: https://ideone.com/g6Vmwi

C++ : Headers Including each others causes an Error class has not been declared [duplicate]

To break the circular dependency, you need to add a forward declaration of the depended-on classes to each of your header files, like this:

#ifndef A_H
#define A_H
#include <iostream>

class CRoom;

class CPlay {
...

and:

#ifndef D_H
#define D_H
#include <iostream>

class CPlay;

class CRoom {
...

Note that this allows you to remove the #includes from the header files themselves.

C++ circular header includes [duplicate]

Is there a way to forward declare these classes in a way that will allow me to not care about the order in which I include the header files in my main.cpp?

since you are dealing with simple pointers only, you can use a forward declaration here in both cases:

FooA.h

#ifndef H_FOOA
#define H_FOOA

// #include "foob.h" << not needed!
class FooB; // << substitute with a forward declaration of FooB

class FooA{
public:
FooB *fooB;
};
#endif

FooB.h

#ifndef H_FOOB
#define H_FOOB

class FooA;
class FooB{
public:
FooA *fooA;
};
#endif

Avoiding Circular Dependencies of header files [duplicate]

If you have circular dependency then you doing something wrong.

As for example:

foo.h
-----
class foo {
public:
bar b;
};

bar.h
-----
class bar {
public:
foo f;
};

Is illegal you probably want:

foo.h
-----
class bar; // forward declaration
class foo {
...
bar *b;
...
};

bar.h
-----
class foo; // forward declaration
class bar {
...
foo *f;
...
};

And this is ok.

General rules:

  1. Make sure each header can be included on its own.
  2. If you can use forward declarations use them!


Related Topics



Leave a reply



Submit