I'm not quite sure I understand your problem, but if you're talking about what I think you're talking about, #include guards or the #pragma once preprocessor directive should help. The latter is faster but non-standard, so only use it if you're not terribly concerned with portability.
Actually I've been using #pragma once nicely and it did it's job well. However, when you write something like
##### classA.h ####
#include "classB.h"
class A
{
B Member;
};
#### classB.h ####
#include "classA.h"
class B
{
A Member;
};
MSVC will get confused.
I solved the issue after having an epiphany 15 minutes ago by using only pointers in the declaration of the classes. To declare a pointer you don't have to have included the class definition since the compiler doesn't care what the pointed to object looks like. In the .cpp file, where I actually use the pointer, I can include however many .h files I want.
So the above example looks like this now:
#### classA.h ####
class B;
class A
{
B* pMember;
};
#### classA.cpp ###
#include "classA.h"
#include "classB.h"
// Have fun with both A and B here!
#### classB.h ####
class A;
class B
{
A* pMember;
}
#### classB.cpp ####
#include "classB.h"
#include "classA.h"
// Have fun with both A and B here!
I've been looking at the issue above for 2 days straight, alternating between looking at google's unrelated search results about people who had spelling mistakes in their declarations and fudging around with my code by myself.
It's wonderous how suddenly you see a solution to problem you've been looking at for so long.
Think about what you're doing here. In order to allocate space for an A, the compiler needs to know what sizeof(A) is - its size in bytes. Since A has a B member that means it needs to know sizeof(B) in order to calculate sizeof(A). B in turn has an A member, so to calculate sizeof(B) it needs to know sizeof(A) which... uh oh. Round we go. See how this doesn't make any sense?
With pointers on the other hand, the compiler knows that sizeof(<any type>*) will always equal N bytes for a given platform (4 for x86, 8 for x86_64). There is no circular dependency between the types.
With C/C++ it's important to remember that a declaration on the form "A variable;" will create a variable directly on the stack. Unlike C# or Java it will not create a reference to a variable on the heap. This means the type's size must be known at the point of declaration. Otherwise the compiler can't know how much space to allocate on the stack.
I understand the issue. But I don't want to deal with it. I want to work on my program, not work around the short-comings of the compiler.
I realize that C++ has the tendency to sacrifice safety over performance and I'm okay with the fact that I can shoot myself in the foot when I want to. I guess I'd just like a language somewhere between C++ and Java that writes proper executable files but allows me to think in my own class-structure rather than the binary representation of it in memory at run-time.
It's not a short-coming. It's a logical error. You have to deal with it. What you're trying to do does not make sense. You cannot construct a type that (effectively) contains itself. It can refer to itself through a pointer or a reference, but it cannot contain itself.
That may be the case in the example above. But when the dependency is based on e.g. the return type of a member-function the logical error isn't as obvious. As an example, class A can't have a member function "B createB()" if B has a member function "A createA()". The complete definition of B has to be ready when the definition of A is being processed, but to complete B class A has to be completed first.
Actually, that case is also fine as long as you forward-declare A and B instead of circularly including their headers. You'll need to define the functions in a translation unit where a complete definition of both classes is visible though. Usually you'd just #include both classA.h and classB.h in classA.cpp and classB.cpp, and then define createA and createB in those translation units respectively.
Anyway, if your complaint was more of a general one against the C++ compilation model you will get no argument from me. My paragraph above provides ample evidence that it is unwieldy. I just got caught up in the specifics of your examples.
It does make some sort of perverse sense once you wrap your head around it though, so there's that.
I dunno if you guys listen to any programmer podcasts, but I'd suggest giving a shot to This Developer's Life, which started this... eh, last... year. It's very good and quite partisan in its affiliations and doesn't focus on any specific technology or platform. Anyway just thought I'd suggest it.