This is a seemingly simple problem from C++ Primer Exercise 12.11
Exercise: Given C++ classes X and Y: X has a pointer to Y, and Y has an object of type X And (this is my personal requirement!): X and Y in separate files!
(So this is all about forward declaration and incomplete type...)
Currently it does not compile...
How can one get this to compile?
As for why including Y.h works, what's happening is this:
main.cpp) includes y.h
y.h) includes x.h
x.h) x.h includes y.h (skipped because y.h was already included)
x.h) X class fully defined
y.h) Y class fully defined
All x.h needs is a forward declaration. It should not be #including y.h... at least not before class X is fully defined.
Well, this explanation can get rather long but I'll give it a shot (trying not to get side-tracked into all possible cases). I'll start at the beginning, too.
First, the compiler compiles a translation unit into an object file. You can see this in your makefile--each object file depends on a source file and a header file1. This separation makes the distinction between declarations and definitions2. Note that the source file must include3 the header file.
In order to use a user-defined type (class) in a translation unit you have to have it's definition (from the header). In order to build the executable, it also needs to have the object file that contains the method definitions. Those are linked in after compilation from the object files.
Now, if you aren't going to "use" the type but rather just hold a reference or pointer to it, you don't need the definition--you only need the declaration, that says it exists. So, in order to store a pointer to a Y in class X, you (at minimum) must provide the forward declaration.
Contrastingly, if you are going to use the type by storing an object, like X in class Y, the compiler needs to know the definition. That requires the header inclusion.
Now, to avoid ambiguity, the executable must also adhere to the One Definition Rule (ODR). Since headers can be included all over the place, inclusion guards are required to prevent multiple definitions (for things defined in headers, such as classes or templates).
I hope you find this explanation is accurate and informative. Let me know if you have any questions!
1 This is called the inclusion model. It's often associated with defining all templates in header files. 2 This terminology is very important. Declarations say there is something that exists and definitions are typically more along the lines of you can use it and/or how it works. It can get confusing because class definitions are quite different from method definitions (I don't want to get into this). 3 Preprocessor includes are replaced with the included files contents verbatim. Think of it as pasting the included file to result in one big text file before compilation.
Looks good. BTW, the best practice is to always use the minimal approach--forward declare anytime that you can instead of including the header. That will cut down on dependencies, which can speed up compilation time.
Is it possible to dereference the pointer to Y? See line 11 below.
Can one get that to compile?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
//===== X.h =============================
#ifndef X_h
#define X_h
class Y;
class X
{
public:
X();
void dereference_y_pointer() { i = y->i; } // new line! can this be done? -> currently: compile problems
int i;
Y *y;
};
#endif
In that case you would have to replace the forward declaration with the include. However, what you should prefer (if the method need not be inline) is to put the definition of that method in the source file and include the header there.
I cannot get it to compile by replacing the forward declaration with the include.
And I cannot get it to compile if declaration and definition are (inline) in the header only.
Perhaps in this particular case, the definition must be in the source (X.cc), as is shown in the working code above.
?
If in addition, we dereference the pointer to Y within class X, then this has to be done in the source file of X (X.cc), where we then also need to include the header of Y.
See working solution here: http://www.cplusplus.com/forum/general/46238/#msg251171
It turns out, that one can also dereference the pointer to Y, from the header (as is needed for inline): how to do this can be seen in the following thread: http://www.cplusplus.com/forum/general/47419/