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 #include
s 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:
- Make sure each header can be included on its own.
- If you can use forward declarations use them!
Related Topics
What Does the Single Ampersand After the Parameter List of a Member Function Declaration Mean
Templated Check For the Existence of a Class Member Function
Why Do We Need Virtual Functions in C++
What Does "Dereferencing" a Pointer Mean
Best Practices For Circular Shift (Rotate) Operations in C++
Raii and Smart Pointers in C++
Check If a Class Has a Member Function of a Given Signature
Is Signed Integer Overflow Still Undefined Behavior in C++
Is There a Standard Sign Function (Signum, Sgn) in C/C++
The Definitive C++ Book Guide and List
When Should Static_Cast, Dynamic_Cast, Const_Cast, and Reinterpret_Cast Be Used
Why Does an Overridden Function in the Derived Class Hide Other Overloads of the Base Class
How to Convert a Std::String to Const Char* or Char*
What Is the Type of String Literals in C and C++