Resolve build errors due to circular dependency amongst classes
up vote
287
down vote
favorite
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;
}
c++ compiler-errors circular-dependency c++-faq
add a comment |
up vote
287
down vote
favorite
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;
}
c++ compiler-errors circular-dependency c++-faq
17
When working with Visual Studio, the /showIncludes flag helps a lot to debug this kind of problems.
– wil
Sep 12 '12 at 3:08
2
The above link appears to have moved. Here's the new link for /showIncludes on MSDN
– Tas
Oct 26 '17 at 4:30
add a comment |
up vote
287
down vote
favorite
up vote
287
down vote
favorite
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;
}
c++ compiler-errors circular-dependency c++-faq
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;
}
c++ compiler-errors circular-dependency c++-faq
c++ compiler-errors circular-dependency c++-faq
edited Oct 12 '17 at 14:20
StoryTeller
89.9k12180246
89.9k12180246
asked Mar 9 '09 at 10:57
Autodidact
16.5k135572
16.5k135572
17
When working with Visual Studio, the /showIncludes flag helps a lot to debug this kind of problems.
– wil
Sep 12 '12 at 3:08
2
The above link appears to have moved. Here's the new link for /showIncludes on MSDN
– Tas
Oct 26 '17 at 4:30
add a comment |
17
When working with Visual Studio, the /showIncludes flag helps a lot to debug this kind of problems.
– wil
Sep 12 '12 at 3:08
2
The above link appears to have moved. Here's the new link for /showIncludes on MSDN
– Tas
Oct 26 '17 at 4:30
17
17
When working with Visual Studio, the /showIncludes flag helps a lot to debug this kind of problems.
– wil
Sep 12 '12 at 3:08
When working with Visual Studio, the /showIncludes flag helps a lot to debug this kind of problems.
– wil
Sep 12 '12 at 3:08
2
2
The above link appears to have moved. Here's the new link for /showIncludes on MSDN
– Tas
Oct 26 '17 at 4:30
The above link appears to have moved. Here's the new link for /showIncludes on MSDN
– Tas
Oct 26 '17 at 4:30
add a comment |
9 Answers
9
active
oldest
votes
up vote
233
down vote
accepted
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.
15
"Telling the compiler about B" is known as a forward declaration of B.
– Peter Ajtai
Nov 17 '10 at 1:57
6
Omg! totally missed the fact that references are known in terms of occupied space. Finally, now I can design properly!
– kellogs
Nov 7 '11 at 2:31
30
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 3 '15 at 13:56
2
@sydan: You can't. Resolving circular dependencies requires out-of-class definitions.
– Ben Voigt
Apr 11 '15 at 14:03
|
show 4 more comments
up vote
93
down vote
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;
}
8
This is the best answer
– rank1
Apr 17 '13 at 15:26
Thanks. This solved the problem easily. I simply moved the circular includes to the .cpp files.
– Lenar Hoyt
Oct 4 '14 at 18:16
yes, this is how you can solve circular dependecies
– Guru
Oct 16 '14 at 4:41
3
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 1 '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 at 4:25
add a comment |
up vote
17
down vote
Things to remember:
- This won't work if
class A
has an object ofclass 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:
- How can I create two classes that both know about each other?
- What special considerations are needed when forward declarations are used with member objects?
- What special considerations are needed when forward declarations are used with inline functions?
1
the links you provided dont work anymore, do you happen to know the new ones to refer to?
– Ramya Rao
Feb 22 '17 at 22:11
add a comment |
up vote
11
down vote
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
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 includesB.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
add a comment |
up vote
11
down vote
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
ora.cc
did includeb.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).
- compilation of A will terminate with an error once it gets to the conflicting declaration/definition of
- 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 rebuildA
(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.
- build tools relying on
If A's code has template specialisations / "traits" for the old B
, they won't take effect.
This is a really clean way to handle the forward declarations. The only "disadvantage" would be in the extra files. I assume you always includea.fwd.h
ina.h
, to assure they stay in sync. The example code is missing where these classes are used.a.h
andb.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. Whereb.h
includesa.h
andmain.cpp
includesb.h
– Farway
May 5 '17 at 16:37
2
@Farway Right on all counts. I didn't bother showingmain.cpp
, but nice that you've documented what it should contain in your comment. Cheers
– Tony Delroy
May 5 '17 at 20:42
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 at 5:06
add a comment |
up vote
7
down vote
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;
}
2
Please note that use of interfaces andvirtual
has runtime performance impacts.
– cemper93
Jun 22 '16 at 19:10
add a comment |
up vote
3
down vote
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.
add a comment |
up vote
2
down vote
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;
}
add a comment |
up vote
0
down vote
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
andB
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.
Does this have any advantages over Tony Delroy's solution? 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 Turati
Nov 20 at 11:33
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 whereA
's andB
's definition is available, forward declaration is not enough).
– geza
Nov 20 at 11:50
add a comment |
protected by StoryTeller Oct 12 '17 at 14:13
Thank you for your interest in this question.
Because it has attracted low-quality or spam answers that had to be removed, posting an answer now requires 10 reputation on this site (the association bonus does not count).
Would you like to answer one of these unanswered questions instead?
9 Answers
9
active
oldest
votes
9 Answers
9
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
233
down vote
accepted
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.
15
"Telling the compiler about B" is known as a forward declaration of B.
– Peter Ajtai
Nov 17 '10 at 1:57
6
Omg! totally missed the fact that references are known in terms of occupied space. Finally, now I can design properly!
– kellogs
Nov 7 '11 at 2:31
30
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 3 '15 at 13:56
2
@sydan: You can't. Resolving circular dependencies requires out-of-class definitions.
– Ben Voigt
Apr 11 '15 at 14:03
|
show 4 more comments
up vote
233
down vote
accepted
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.
15
"Telling the compiler about B" is known as a forward declaration of B.
– Peter Ajtai
Nov 17 '10 at 1:57
6
Omg! totally missed the fact that references are known in terms of occupied space. Finally, now I can design properly!
– kellogs
Nov 7 '11 at 2:31
30
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 3 '15 at 13:56
2
@sydan: You can't. Resolving circular dependencies requires out-of-class definitions.
– Ben Voigt
Apr 11 '15 at 14:03
|
show 4 more comments
up vote
233
down vote
accepted
up vote
233
down vote
accepted
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.
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.
edited May 23 '17 at 12:02
Community♦
11
11
answered Mar 9 '09 at 21:15
Roosh
2,3902112
2,3902112
15
"Telling the compiler about B" is known as a forward declaration of B.
– Peter Ajtai
Nov 17 '10 at 1:57
6
Omg! totally missed the fact that references are known in terms of occupied space. Finally, now I can design properly!
– kellogs
Nov 7 '11 at 2:31
30
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 3 '15 at 13:56
2
@sydan: You can't. Resolving circular dependencies requires out-of-class definitions.
– Ben Voigt
Apr 11 '15 at 14:03
|
show 4 more comments
15
"Telling the compiler about B" is known as a forward declaration of B.
– Peter Ajtai
Nov 17 '10 at 1:57
6
Omg! totally missed the fact that references are known in terms of occupied space. Finally, now I can design properly!
– kellogs
Nov 7 '11 at 2:31
30
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 3 '15 at 13:56
2
@sydan: You can't. Resolving circular dependencies requires out-of-class definitions.
– Ben Voigt
Apr 11 '15 at 14:03
15
15
"Telling the compiler about B" is known as a forward declaration of B.
– Peter Ajtai
Nov 17 '10 at 1:57
"Telling the compiler about B" is known as a forward declaration of B.
– Peter Ajtai
Nov 17 '10 at 1:57
6
6
Omg! totally missed the fact that references are known in terms of occupied space. Finally, now I can design properly!
– kellogs
Nov 7 '11 at 2:31
Omg! totally missed the fact that references are known in terms of occupied space. Finally, now I can design properly!
– kellogs
Nov 7 '11 at 2:31
30
30
But still You cannot use any function on B (as in the question _b->Printt())
– rank1
Apr 17 '13 at 11:02
But still You cannot use any function on B (as in the question _b->Printt())
– rank1
Apr 17 '13 at 11:02
3
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 3 '15 at 13:56
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 3 '15 at 13:56
2
2
@sydan: You can't. Resolving circular dependencies requires out-of-class definitions.
– Ben Voigt
Apr 11 '15 at 14:03
@sydan: You can't. Resolving circular dependencies requires out-of-class definitions.
– Ben Voigt
Apr 11 '15 at 14:03
|
show 4 more comments
up vote
93
down vote
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;
}
8
This is the best answer
– rank1
Apr 17 '13 at 15:26
Thanks. This solved the problem easily. I simply moved the circular includes to the .cpp files.
– Lenar Hoyt
Oct 4 '14 at 18:16
yes, this is how you can solve circular dependecies
– Guru
Oct 16 '14 at 4:41
3
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 1 '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 at 4:25
add a comment |
up vote
93
down vote
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;
}
8
This is the best answer
– rank1
Apr 17 '13 at 15:26
Thanks. This solved the problem easily. I simply moved the circular includes to the .cpp files.
– Lenar Hoyt
Oct 4 '14 at 18:16
yes, this is how you can solve circular dependecies
– Guru
Oct 16 '14 at 4:41
3
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 1 '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 at 4:25
add a comment |
up vote
93
down vote
up vote
93
down vote
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;
}
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;
}
edited Jan 5 '15 at 12:31
answered Mar 9 '09 at 10:57
Autodidact
16.5k135572
16.5k135572
8
This is the best answer
– rank1
Apr 17 '13 at 15:26
Thanks. This solved the problem easily. I simply moved the circular includes to the .cpp files.
– Lenar Hoyt
Oct 4 '14 at 18:16
yes, this is how you can solve circular dependecies
– Guru
Oct 16 '14 at 4:41
3
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 1 '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 at 4:25
add a comment |
8
This is the best answer
– rank1
Apr 17 '13 at 15:26
Thanks. This solved the problem easily. I simply moved the circular includes to the .cpp files.
– Lenar Hoyt
Oct 4 '14 at 18:16
yes, this is how you can solve circular dependecies
– Guru
Oct 16 '14 at 4:41
3
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 1 '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 at 4:25
8
8
This is the best answer
– rank1
Apr 17 '13 at 15:26
This is the best answer
– rank1
Apr 17 '13 at 15:26
Thanks. This solved the problem easily. I simply moved the circular includes to the .cpp files.
– Lenar Hoyt
Oct 4 '14 at 18:16
Thanks. This solved the problem easily. I simply moved the circular includes to the .cpp files.
– Lenar Hoyt
Oct 4 '14 at 18:16
yes, this is how you can solve circular dependecies
– Guru
Oct 16 '14 at 4:41
yes, this is how you can solve circular dependecies
– Guru
Oct 16 '14 at 4:41
3
3
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 1 '16 at 12:55
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 1 '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 at 4:25
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 at 4:25
add a comment |
up vote
17
down vote
Things to remember:
- This won't work if
class A
has an object ofclass 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:
- How can I create two classes that both know about each other?
- What special considerations are needed when forward declarations are used with member objects?
- What special considerations are needed when forward declarations are used with inline functions?
1
the links you provided dont work anymore, do you happen to know the new ones to refer to?
– Ramya Rao
Feb 22 '17 at 22:11
add a comment |
up vote
17
down vote
Things to remember:
- This won't work if
class A
has an object ofclass 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:
- How can I create two classes that both know about each other?
- What special considerations are needed when forward declarations are used with member objects?
- What special considerations are needed when forward declarations are used with inline functions?
1
the links you provided dont work anymore, do you happen to know the new ones to refer to?
– Ramya Rao
Feb 22 '17 at 22:11
add a comment |
up vote
17
down vote
up vote
17
down vote
Things to remember:
- This won't work if
class A
has an object ofclass 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:
- How can I create two classes that both know about each other?
- What special considerations are needed when forward declarations are used with member objects?
- What special considerations are needed when forward declarations are used with inline functions?
Things to remember:
- This won't work if
class A
has an object ofclass 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:
- How can I create two classes that both know about each other?
- What special considerations are needed when forward declarations are used with member objects?
- What special considerations are needed when forward declarations are used with inline functions?
edited Mar 1 at 0:05
Ken Y-N
7,383134469
7,383134469
answered Mar 9 '09 at 11:07
dirkgently
89.5k15110176
89.5k15110176
1
the links you provided dont work anymore, do you happen to know the new ones to refer to?
– Ramya Rao
Feb 22 '17 at 22:11
add a comment |
1
the links you provided dont work anymore, do you happen to know the new ones to refer to?
– Ramya Rao
Feb 22 '17 at 22:11
1
1
the links you provided dont work anymore, do you happen to know the new ones to refer to?
– Ramya Rao
Feb 22 '17 at 22:11
the links you provided dont work anymore, do you happen to know the new ones to refer to?
– Ramya Rao
Feb 22 '17 at 22:11
add a comment |
up vote
11
down vote
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
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 includesB.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
add a comment |
up vote
11
down vote
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
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 includesB.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
add a comment |
up vote
11
down vote
up vote
11
down vote
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
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
edited Mar 10 '09 at 0:18
answered Mar 9 '09 at 21:25
epatel
42.2k1698136
42.2k1698136
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 includesB.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
add a comment |
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 includesB.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
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
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
What happens if a user includes
B.h
first?– Mr Fooz
Mar 18 '14 at 16:00
3
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
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
add a comment |
up vote
11
down vote
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
ora.cc
did includeb.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).
- compilation of A will terminate with an error once it gets to the conflicting declaration/definition of
- 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 rebuildA
(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.
- build tools relying on
If A's code has template specialisations / "traits" for the old B
, they won't take effect.
This is a really clean way to handle the forward declarations. The only "disadvantage" would be in the extra files. I assume you always includea.fwd.h
ina.h
, to assure they stay in sync. The example code is missing where these classes are used.a.h
andb.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. Whereb.h
includesa.h
andmain.cpp
includesb.h
– Farway
May 5 '17 at 16:37
2
@Farway Right on all counts. I didn't bother showingmain.cpp
, but nice that you've documented what it should contain in your comment. Cheers
– Tony Delroy
May 5 '17 at 20:42
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 at 5:06
add a comment |
up vote
11
down vote
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
ora.cc
did includeb.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).
- compilation of A will terminate with an error once it gets to the conflicting declaration/definition of
- 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 rebuildA
(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.
- build tools relying on
If A's code has template specialisations / "traits" for the old B
, they won't take effect.
This is a really clean way to handle the forward declarations. The only "disadvantage" would be in the extra files. I assume you always includea.fwd.h
ina.h
, to assure they stay in sync. The example code is missing where these classes are used.a.h
andb.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. Whereb.h
includesa.h
andmain.cpp
includesb.h
– Farway
May 5 '17 at 16:37
2
@Farway Right on all counts. I didn't bother showingmain.cpp
, but nice that you've documented what it should contain in your comment. Cheers
– Tony Delroy
May 5 '17 at 20:42
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 at 5:06
add a comment |
up vote
11
down vote
up vote
11
down vote
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
ora.cc
did includeb.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).
- compilation of A will terminate with an error once it gets to the conflicting declaration/definition of
- 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 rebuildA
(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.
- build tools relying on
If A's code has template specialisations / "traits" for the old B
, they won't take effect.
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
ora.cc
did includeb.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).
- compilation of A will terminate with an error once it gets to the conflicting declaration/definition of
- 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 rebuildA
(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.
- build tools relying on
If A's code has template specialisations / "traits" for the old B
, they won't take effect.
edited Jul 5 '16 at 12:56
answered Mar 23 '15 at 11:53
Tony Delroy
82.8k9124186
82.8k9124186
This is a really clean way to handle the forward declarations. The only "disadvantage" would be in the extra files. I assume you always includea.fwd.h
ina.h
, to assure they stay in sync. The example code is missing where these classes are used.a.h
andb.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. Whereb.h
includesa.h
andmain.cpp
includesb.h
– Farway
May 5 '17 at 16:37
2
@Farway Right on all counts. I didn't bother showingmain.cpp
, but nice that you've documented what it should contain in your comment. Cheers
– Tony Delroy
May 5 '17 at 20:42
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 at 5:06
add a comment |
This is a really clean way to handle the forward declarations. The only "disadvantage" would be in the extra files. I assume you always includea.fwd.h
ina.h
, to assure they stay in sync. The example code is missing where these classes are used.a.h
andb.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. Whereb.h
includesa.h
andmain.cpp
includesb.h
– Farway
May 5 '17 at 16:37
2
@Farway Right on all counts. I didn't bother showingmain.cpp
, but nice that you've documented what it should contain in your comment. Cheers
– Tony Delroy
May 5 '17 at 20:42
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 at 5:06
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 5 '17 at 16:37
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 5 '17 at 16:37
2
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 5 '17 at 20:42
@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 5 '17 at 20:42
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 at 5:06
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 at 5:06
add a comment |
up vote
7
down vote
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;
}
2
Please note that use of interfaces andvirtual
has runtime performance impacts.
– cemper93
Jun 22 '16 at 19:10
add a comment |
up vote
7
down vote
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;
}
2
Please note that use of interfaces andvirtual
has runtime performance impacts.
– cemper93
Jun 22 '16 at 19:10
add a comment |
up vote
7
down vote
up vote
7
down vote
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;
}
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;
}
edited Apr 5 '15 at 20:31
answered Dec 15 '13 at 18:12
Eduard Wirch
7,33575065
7,33575065
2
Please note that use of interfaces andvirtual
has runtime performance impacts.
– cemper93
Jun 22 '16 at 19:10
add a comment |
2
Please note that use of interfaces andvirtual
has runtime performance impacts.
– cemper93
Jun 22 '16 at 19:10
2
2
Please note that use of interfaces and
virtual
has runtime performance impacts.– cemper93
Jun 22 '16 at 19:10
Please note that use of interfaces and
virtual
has runtime performance impacts.– cemper93
Jun 22 '16 at 19:10
add a comment |
up vote
3
down vote
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.
add a comment |
up vote
3
down vote
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.
add a comment |
up vote
3
down vote
up vote
3
down vote
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.
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.
edited Jan 1 '16 at 12:43
answered Oct 9 '15 at 21:21
Tatyana
513
513
add a comment |
add a comment |
up vote
2
down vote
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;
}
add a comment |
up vote
2
down vote
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;
}
add a comment |
up vote
2
down vote
up vote
2
down vote
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;
}
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;
}
edited Dec 10 '14 at 17:32
answered Dec 10 '14 at 16:48
madx
3,86823344
3,86823344
add a comment |
add a comment |
up vote
0
down vote
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
andB
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.
Does this have any advantages over Tony Delroy's solution? 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 Turati
Nov 20 at 11:33
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 whereA
's andB
's definition is available, forward declaration is not enough).
– geza
Nov 20 at 11:50
add a comment |
up vote
0
down vote
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
andB
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.
Does this have any advantages over Tony Delroy's solution? 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 Turati
Nov 20 at 11:33
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 whereA
's andB
's definition is available, forward declaration is not enough).
– geza
Nov 20 at 11:50
add a comment |
up vote
0
down vote
up vote
0
down vote
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
andB
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.
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
andB
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.
answered Jul 5 at 7:02
geza
11.8k32771
11.8k32771
Does this have any advantages over Tony Delroy's solution? 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 Turati
Nov 20 at 11:33
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 whereA
's andB
's definition is available, forward declaration is not enough).
– geza
Nov 20 at 11:50
add a comment |
Does this have any advantages over Tony Delroy's solution? 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 Turati
Nov 20 at 11:33
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 whereA
's andB
's definition is available, forward declaration is not enough).
– geza
Nov 20 at 11:50
Does this have any advantages over Tony Delroy's solution? 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 Turati
Nov 20 at 11:33
Does this have any advantages over Tony Delroy's solution? 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 Turati
Nov 20 at 11:33
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 at 11:50
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 at 11:50
add a comment |
protected by StoryTeller Oct 12 '17 at 14:13
Thank you for your interest in this question.
Because it has attracted low-quality or spam answers that had to be removed, posting an answer now requires 10 reputation on this site (the association bonus does not count).
Would you like to answer one of these unanswered questions instead?
17
When working with Visual Studio, the /showIncludes flag helps a lot to debug this kind of problems.
– wil
Sep 12 '12 at 3:08
2
The above link appears to have moved. Here's the new link for /showIncludes on MSDN
– Tas
Oct 26 '17 at 4:30