Question

I'm trying to build a solution which has three files. With main.cpp it is four files.

Entity.h

#pragma once

#include "SystemBase.h"

namespace Engine {

class Entity {
public:
    Entity() { }

    void s(SystemBase* sb) { }
};
}

SubscribersList.h

#pragma once

#include "SystemBase.h"
#include "Entity.h"

namespace Engine {

class SubscribersList {
    friend SystemBase;

public:
    SubscribersList() { }

    void f(Entity* e) { }
};
}

SystemBase.h

 #pragma once

 #include "SubscribersList.h"
 #include "Entity.h"

 namespace Engine {

class SystemBase {
public:
    SystemBase() { }

    void g(Entity* e) { }

private:
    SubscribersList m;

};
}

Don't focus on the body's of methods in the headers. It is just to keep things simple. I found two ways to build the solution. 1. Write the word class before all class names. But it crashes when I try to separate the realization from prototypes. 2. Write all code in one file. I don't/won't write the keyword class before all class names to build the solution, and certainly I don't/won't write a big project in one file. So why I can't build it? What is the magic?!

Was it helpful?

Solution

To understand the problem of cyclic header dependency we first need understand the difference between a class declaration and definition and the concept of incomplete types.

A prototype or forward declaration of a type Type is written as:

class Type;

Such a forward declaration allows you to create pointers and reference to that type. You cannot however instantiate, dereference pointers to or use a reference to Type until its full type is declared.

A declaration for Type could be written as:

class AnotherType;

class Type {
public:
    void aMemberFunc();
private:
    AnotherType *m_theOtherThing;
};

Now we have the declaration instances can be created and pointers to Type can be dereferenced.

However before m_theOtherThing is dereferenced or instanciated AnotherType must be fully declared.

class AnotherType {
    Type m_aType;
}

Should do, which gives us both the full declaration and definition of AnotherType.

That allows to continue on to write the definition of Type::aMemberFunc:

void Type::aMemberFunc() {
    m_theOtherThing = new AnotherType();
}

If instead of presenting this code to the compiler in this order we instead presented the full declarations of Type and AnotherType up front:

class Type {
public:
    void aMemberFunc();
private:
    AnotherType *m_theOtherThing;
};

class AnotherType {
    Type m_aType;
}

Then AnotherType *m_theOtherThing; will fail to compile as AnotherType has not been declared or forward declared by that point.

Switching the order gives:

class AnotherType {
    Type m_aType;
}

class Type {
public:
    void aMemberFunc();
private:
    AnotherType *m_theOtherThing;
};

Now Type m_aType; will not compile as Type has not been declared. A forward declaration would not do in this case.

Using #pragma once instead of header guards does not in anyway change the problem. #pragma once only ensures the header is include just once it does not effect the order the compiler processes the code otherwise. It certainly does not allow the compiler to ignore undefined types when it reaches them.

For this kind of class structure there is no way for the compiler to be able to process it without the use for forward declarations.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top