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.