Include Files – Best Practice
Include files for C++ (and C) are a simple idea but they are often misused.
The main intent of an include file is communication. While that is a woolly statement, it is quite true. A header file communicates the interface of a class/module to it’s clients and nothing more.
Anything that a client needs to know in order to use a class/module (I’ll use class from now on to mean either) should be present in the header file, anything that is used internally by the class should not
All too often we use header files as a convenient place to “bung” all our constants when developing a class, without any regard for who will use them (you only need to look at windows.h to see what I mean).
Stripping down an include file to just the bare interface can have numerous benefits, the first one being that the header file is less cluttered and easier to read. This means that someone using this header file will have a much easier time, something we all strive for.
Sometimes we nest include files inside each other. Now while this is fine (the standard even allows this), caution should be execised when doing so. There are at least two reasons while a header file should not include other headers and only one reason it they should.
Reasons not to include
- The header is being included either because our class needs it internally OR the clients might need it
Prime examples of this are including iostream because our class needs to use std::cout or even “The client classes are bound to want cout, so I’ll include it for them.”
If our class needs std::cout then include it in the cpp file. By including iostream in the header we may be duplicating an include that the client has already included. I know mutual inclusion lockout can protect against this but it’s not automatic and can be forgotten.
Including lots of uneccesary headers can hinder the development effort. Suppose that you want to compile a small subset of a S/W system (maybe even a single source file) in isolation to help track down a bug. This can be difficult and time consuming, if you need to copy across lots of extra files just to compile.
A more serious issue here is that in a large S/W system if a set of include files nest very deeply there is a real danger that the compiler’s will run out of heap space and fail to compile what is an otherwise compliant source file.
- The header file is included because the class uses type declarations defined in the included file.
To put this in simpler terms, class A has a member variable of type class B and so it must include class B’s header file.
This is true because when compiling any code which uses class A the compiler needs to know how big an instance of class A is so it can allocate the correct amount of stack/heap space. To do this it needs to know how big each of the member variables are.
If they are built in types int, char, float etc there is no problem. However for a user defined class then it needs to inspect the class declaration which is in the header file for that class.
This can be avoided however. If the member variable (of type class B) is a pointer or a reference to a class B then there is no need to include class B’s header.
A pointer or a reference is just a memory address and the compiler knows how much space to allocate for those.
Reasons to include
The only real reason to include a header B inside header A is if header A needs header B in order to compile it in isolation. Now I know we don’t compile header files.
BUT if you were to include header A into an empty cpp file and compile that, any missing types etc that the compiler complains about need to be included into header A.
There you have it, “My simple guide to include files”.