Spectral
Spectral

Reputation: 15

Circular include dependency/Forward Declarations

All the solutions to circular include dependencies I've seen just say in "this particular case" the full class definition isn't necessary since "you" are only using pointers to that class.

I encountered this problem and fixed it using forward declarations.

I am wondering what are you supposed to do when you need the specific definition of the other class in both classes.

Also, why does using a pointer to the class allow you use a forward declaration instead of a class definition?

Upvotes: 0

Views: 100

Answers (2)

Koby Duck
Koby Duck

Reputation: 1138

I am wondering what are you supposed to do when you need the specific definition of the other class in both classes.

It can be done with forward declarations and deferred definitions in modern compilers. Many older compilers only allow pointers & references to forward declared types.

Here's a contrived example:

A.hpp

class B;

class A
{
public:
    int32_t Value;

    A(int32_t value) : Value(value) { }

    int32_t Add(B b) const;
}

B.hpp

#include "A.hpp"

class B
{
public:
    int32_t Value;

    B(int32_t value) : Value(value) { }

    int32_t Sub(A a) const;
}

AB.hpp

#include "A.hpp"
#include "B.hpp"

inline int32_t A::Add(B b) const
{
    return this->Value + b.Value;
}

inline int32_t B::Sub(A a) const
{
    return this->Value - a.Value;
}

Also, why does using a pointer to the class allow you use a forward declaration instead of a class definition?

Forward declarations are just names to the compiler. The concept exists so you can use types that haven't been defined yet. This is necessary because of the way C++ parses code, an artifact of the C language it inherits a great deal from. C++ parsers are really just forward-only text processors that inject text when you #include and use macros. It's a conceptually simple model that made C/C++ compilers easier to write in the early days. Contrast this to C#/Java where you just use using/import and happily create circular dependencies between classes with simple syntax.

Pointers are really just integers, similar to short and int, but with special semantics enforced by the language and a fixed size known at compile time based on the CPU architecture. This makes pointer declarations very simple for compilers to deal with.

Forward declaration facilitates circular dependencies and implementation hiding(which also happens to speed up compilation time). Consider the pimpl idiom. Without forward declarations there's no type-safe way to hide implementation details.

Upvotes: 0

jakedipity
jakedipity

Reputation: 900

In what cases would you need the specification known beforehand for both classes?

One impossible case is the following:

class A
{
    B m_b;
};

class B
{
    A m_a;
};

But this is impossible since the size of class A depends on the size of class B, but the size of class B depends on the size of class A. You'll also get an infinite series A myA; myA.m_b.m_a.m_b.m_a.... when you try to construct either.

If you use pointers, you don't need to know the size of either; a pointer is always the same size depending on the platform your are on. And the series disappears because objects in the heap need to be created explicitly.

Upvotes: 2

Related Questions