salbeira
salbeira

Reputation: 2583

The great c++ forward declaration confusion

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

Answers (3)

Remy Lebeau
Remy Lebeau

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

Jive Dadson
Jive Dadson

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

Miles Budnek
Miles Budnek

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

Related Questions