Reputation: 2583
Consider I have a Class A and a Class B and their corresponding header:
a.h
#ifndef CLASS_A
#define CLASS_A
/* forward declare A */
class A;
/* includes */
#include "b.h"
/* define class A */
class A {
public:
A() : p_b(nullptr) {}
B *p_b;
};
#endif
b.h
#ifndef CLASS_B
#define CLASS_B
/* forward declare B */
class B;
/* includes */
#include "a.h"
/* define class B */
class B {
public:
B() : m_a() {}
A m_a;
};
#endif
This does not work:
To compile the implementation of A into an object-file, I first include a.h, that forward declares A and then includes b.h that then declares and defines B. But when B is defined it does not know the size of A and therefore can not declare an object of A as a member of B.
A however does not need to know the size of B, as it only has a pointer to B and could be completely defined before B get's defined. Therefore the size of B can be completely known before it gets used as a member and the complete declaration SHOULD be fine.
Common sense though tells that the a.c file should always look like this:
#include "a.h"
[...]
Can I actually solve the problem by including b.h before a.h in a.c ? Would this be against some holy convention of having the first line of an implementation file being the include of it's header?
Upvotes: 2
Views: 875
Reputation: 595349
You are using forward declarations in a backwards manner. The code should look more like this instead:
a.h
#ifndef CLASS_A
#define CLASS_A
/* forward declare B */
class B;
/* define class A */
class A {
public:
A() : p_b(nullptr) {}
B *p_b;
};
#endif
b.h
#ifndef CLASS_B
#define CLASS_B
#include "a.h"
/* define class B */
class B {
public:
B() : m_a() {}
A m_a;
};
#endif
a.h
doesn't need to know what B
actually is, since A
contains a B*
pointer and not a B
object. So a.h
should not be using #include "b.h"
at all, it should be forward declaring B
instead.
b.h
does need to know what A
actually is, since B
contains an A
object and not an A*
pointer. So b.h
should be using #include "a.h"
, which already forward declares B
before defining A
, then b.h
finishes defining B
.
a.c
can then use #include "a.h"
to bring in the declaration of A
so it can finish defining the implementation, and it can use #include "b.h"
only if A
's methods need to access members of B
.
Upvotes: 4
Reputation: 17016
Since each class depends upon the other, both classes should be defined in the same header file (and in the same namespace). If for some reason they must be in different header files, this would work.
A.h
#ifndef CLASS_A
#define CLASS_A
class B;
class A {
public:
A() : p_b(nullptr) {}
B *p_b;
};
#endif
B.h
#ifndef CLASS_B
#define CLASS_B
#include "a.h"
class B {
public:
B() : m_a() {}
A m_a;
};
#endif
Upvotes: 0
Reputation: 30494
Lets look at what the compiler sees after the preprocessor has done its thing:
/* forward declare A */
class A;
/* includes */
/* forward declare B */
class B;
/* includes */
/* define class B */
class B {
public:
B() : m_a() {}
A m_a;
};
/* define class A */
class A {
public:
A() : p_b(nullptr) {}
B *p_b;
};
As you can see, B
's class definition comes before A
is fully defined, and so your program is ill-formed.
Here, you only need one forward-declaration (of B
), and it should come just before the definition of A
in A.h:
#ifndef CLASS_A
#define CLASS_A
// Forward declare B so that B* p_b is legal
class B;
// Note that B.h is *not* included here
class A {
public:
A() : p_b(nullptr) {}
B *p_b;
};
#endif
Here, you break the circular include cycle by forward-declaring B
instead of including the full definition of B
. Presumably in A.cpp you would then #include
the full definition of B
so that you can use its members.
Upvotes: 1