How are circular #includes resolved?
Typically you protect your include file with an ifndef/define that corresponds to the file name. This doesn't prevent the file from being included again, but it does prevent the contents (inside the ifndef) from being used and triggering the recursive includes again.
#ifndef HEADER_1_h
#define HEADER_1_h
#include "2.h"
/// rest of 1.h
#endif
#ifndef HEADER_2_h
#define HEADER_2_h
#include "1.h"
// rest of 2.h
#endif
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.
C++ circular header includes
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
How to resolve circular struct dependencies in C
The way to do this is to pre-define the structs using empty definition
typedef struct _A A;
typedef struct _B B;
typedef struct _A {
B *b;
} A;
typedef struct _B {
A a;
} B;
You can put the pre-definitions in a global include file to include from wherever you need.
`
Resolve circular include of 3 classes in C++
As class C
only includes a pointer to class A
, it doesn't need the full definition for either A
or B
(which it doesn't use); so, just a statement declaring A
as a class will suffice:
C.h
class A; // Declare that A is a class - then we can have a pointer to it...
class C {
private:
A* myA;
}
Class B
needs the definition of C
, because it includes an instance of C
; and, like class C
, simply declaring A
as a class (as is done already in C.h
) will do:
B.h
#include "C.h" // Note: This header already declares "A" as a class!
class B {
private:
A* myA;
C myC;
};
Class A
needs the definition of B
, as it contains an instance of B
. But note that B.h
already includes C.h
:
A.h
#include "B.h"
class A {
private:
B myB;
};
Note that any other files that use one or more of A
, B
and C
need only include the A.h
header, as this will itself include the others.
C circular dependency
Seems like you shouldn't need to include anything in any of the files. A forward declaration of the relevant types should be sufficient:
#ifndef MapTest_vertex_h
#define MapTest_vertex_h
struct edgelist;
typedef struct
{
char* name;
float x, y;
edgelist* edges; // C++ only - not C
} vertex;
#endif
etc. In C coding, you have to write:
struct edgelist;
typedef struct
{
char* name;
float x, y;
struct edgelist* edges;
} vertex;
C++ is creating a header to solve circular dependency a good idea?
What you have is fine. The naming suffix I've usually seen is _fwd.h
/_fwd.hpp
/_fwd.hxx
or whatever variant of .h
you like. You can see this in Boost, for example: optional_fwd.hpp.
(Of course reducing circular dependencies is a good goal, but some things are mutually-recursive in nature, like grammars, so you have no choice.)
Related Topics
Concat Two 'Const Char' String Literals
What Is the Type of a String Literal in C++
Pthreads: Thread Starvation Caused by Quick Re-Locking
Blur Effect Over a Qwidget in Qt
Why Does a Std::Atomic Store with Sequential Consistency Use Xchg
Is There an Iterator Across Unique Keys in a Std::Multimap
<Iostream> VS. <Iostream.H> VS. "Iostream.H"
Calling Initializer_List Constructor via Make_Unique/Make_Shared
Read into Std::String Using Scanf
Alternative Function in iOStream.H for Getch() of Conio.H
"If Constexpr" in C++17 Does Not Work in a Non-Templated Function
Vector That Can Have 3 Different Data Types C++
Detect When Multiple Enum Items Map to Same Value
How to Test Whether a C++ Class Has a Default Constructor (Other Than Compiler-Provided Type Traits)
Sfinae Works Differently in Cases of Type and Non-Type Template Parameters