C++ is one of my favorite languages although sometimes even the things we really enjoy can drive us absolutely insane. This is especially true when dealing with header files and proper inclusions which, if you’re just staring out in the world of C++, can be confusing to say the least. Luckily there are a multitude of online resources that help explain how to properly deal with header files in order to avoid the typical roadblocks. The best article I found so far and one I highly recommend reading (and understanding) is on the C++ Forum. Read it, understand it. It will make your life as a C++ programmer easy(er).
So at this point I’m going to make some assumptions:
Now using all of your knowledge of inclusions, forward declares, etc. how would you deal with a scenario where one class (call it our container class) contains an array (lets say a vector) of pointers to a second class. No problem, easy enough. But now let’s assume that each of the classes in the array wants to have a pointer back to the container. Okay, we’re still safe as long as we’re just pointing back to it in which case we can simply use a forward declare in the prototype. But what if we need more? What if you have to access functions within the parent container? You can no longer rely on a forward declare because the compiler has no idea what the functions mean. Similarly you can’t use includes to each class as it would cause a circular inclusion problem. I’ve created simple code sample to help illustrate the problem.
The following C++ program creates a simple container class that contains a vector array of task classes. Each task object can reference back to the parent and query how many tasks are currently running.
The main.cpp file simply starts off the program creating a container class. It populates the container using the addTasks() function then runs the tasks.
Again container.h is pretty straightforward. It contains a vector of task pointers (m_taskList) which will contain our tasks. The thing to note here is that we’re including the “task.h” header since we will be using task objects. If we were just using task pointers, we could get away with a forward declaration but that’s not the case.
Again, there’s nothing magical about container.cpp. We include the loops that generate out task objects and set a value for m_size. This is the variable that we will be querying from out tasks later on. I’ve kept m_size as a constant to keep things simple.
Now we’re starting to get to the good stuff. If you note, here we’re creating a run() function that takes a pointer to our container class as a parameter. In order for our compiler to understand what “container” is, we need to either add a #include “container.h” or we need to forward declare with “class container”. However if we go with the include, we’ll get a compile error. This is because in container.h we included task.h so including container.h in task.h would cause our cyclical inclusion. So we’re forced to pick the forward declare.
And now the world as we know it comes to a grinding halt. If you note line #11 you’ll see that we’re calling the size() function using the container pointer. Had we used a #include to add container, this would have worked but since we can’t use the #include (because of the cyclical inclusion) we’re stuck. The compiler has no idea what “size” is because it gets no insights from a forward declare. All it knows is that somewhere there exists something called a “container” that we can point to.
So how do we solve this? It’s quite easy actually and once you see it light bulbs will hopefully go off. Since the compiler has no idea what functions exist in the container class, we need to specify the container.h include. But since the including it in the header would cause the cyclical inclusion error, we will (wait for it), move the #include “container.h” into the source file. In this case task.cpp. Yup, that’s it. Think about it. The compiler checks the header and sees the forward declaration so it knows what a “container” is in the prototype. Then when it gets to the .cpp file it will pull the full content of the file by linking to the container.h. Here is the updated task.cpp file (note line 2):
Now I know that many would argue that you should not add includes to your source files (other than to the associated .h files), however in this case I can’t see a way around it. Yes there are different ways of organizing code but there will always be scenarios where this type issue comes up. A perfect example would be a threading pool where independent processes require access back to a shared resource (which is where I originally ran into this problem).
Finally, for completeness-sake I’ve included the makefile in case you’re in the mood to play with this yourself. Enjoy.