inclusion of other headers in headers: Do or Don't?
In the last few days I have been running into a question more and more and I'm unable to find a satisfying answer for myself, so here I am, presenting this question to you guys:
Should I avoid including other headers in a header file whenever possible or does it make sense to include headers for structs / classes this header is using anyway?
I started wondering about this when I went through all my header files and got rid of namespace inclusions in them in order to avoid namespace confusion for files including them.
So, doing that, I asked myself: How about header files?
Do I really want to "accidentally" include another header file by including this one?
On the other hand: Do I really want that I have to include that next header explicitly when it is required to use this class properly anyway?
Simple example:
I have a class Box defined in Box.h, using a struct Vector3 defined Vector3.h and a struct Vector4 defined in Vector4.h. Is it a good idea to include both Vector3.h and Vector4.h in Box.h or is it better to provide forward declarations for both structs?
In order to use Box, I'd pretty much have to know Vector3 and Vector4, so I'd have to include Vector3.h and Vector4.h anyway. Also, not including Vector3.h and Vector4.h will mean I'll have to use forward declarations for them. For this simple example, that's okay, but with a growing project I'll end up with forward declarations of a lot of stuff eventually.
So, I guess this comes down to:
Should I include header files for used classes / structs etc whenever possible (without causing a circular inclusion or similar issues) or should I rely on forward declarations as much as possible? Or is there some sweet spot in the middle?
Quote:Original post by matches81
does it make sense to include headers for structs / classes this header is using anyway?
Headers should be self-contained, i.e. they MUST include everything they need.
100% do.
In larger projects, it's especially important to write self-contained headers. That means if you need class Xy, you simply include Xy.h and that's it.
Otherwise, you'd eventually have to track down the 10 other headers it depends on, then the 10 sub-headers they depend on *and* finally include all those headers in the right order because the class in header Xyz might be needing something from header Zyx. All that for any source file and whenever you need to include a new header. Not a very desirable scenario :)
To ensure you build self-contained headers, in each source file belonging to a header, include that header first. If the header isn't self-contained, it will bomb out. Completely eliminates the possibility of accidentally writing headers that are not self-contained or are dependent on include order.
If you have large numbers of headers being included in your headers, that would be more of a design problem. For example, by abstracting and encapsulating internals, a header may only require some basic library headers (say, 'string' and 'vector') whilst the gory details ('windows.h', 'winsock.h') are completely contained in the source file.
In larger projects, it's especially important to write self-contained headers. That means if you need class Xy, you simply include Xy.h and that's it.
Otherwise, you'd eventually have to track down the 10 other headers it depends on, then the 10 sub-headers they depend on *and* finally include all those headers in the right order because the class in header Xyz might be needing something from header Zyx. All that for any source file and whenever you need to include a new header. Not a very desirable scenario :)
To ensure you build self-contained headers, in each source file belonging to a header, include that header first. If the header isn't self-contained, it will bomb out. Completely eliminates the possibility of accidentally writing headers that are not self-contained or are dependent on include order.
If you have large numbers of headers being included in your headers, that would be more of a design problem. For example, by abstracting and encapsulating internals, a header may only require some basic library headers (say, 'string' and 'vector') whilst the gory details ('windows.h', 'winsock.h') are completely contained in the source file.
I usually go with forward declaring as much as possible and including the rest and self-contained headers is a must.
Reducing the amount of actual includes helps with several factors:
* Reduces build times.
* Reduces dependencies and risk of cyclic includes.
* Reduces build times when modifying frequently included headers.
Reducing the amount of actual includes helps with several factors:
* Reduces build times.
* Reduces dependencies and risk of cyclic includes.
* Reduces build times when modifying frequently included headers.
Quote:Original post by Necator
I usually go with forward declaring as much as possible and including the rest and self-contained headers is a must.
Reducing the amount of actual includes helps with several factors:
* Reduces build times.
* Reduces dependencies and risk of cyclic includes.
* Reduces build times when modifying frequently included headers.
Seconded!
Headers must be self-contained. They must include what they need.
Be careful, though. On *really* big projects, even with proper ifdefs and/or pragmas, this can cause compile time to creep up and become annoying. You'll know when you've reached that point and need to refactor header use. Organize things so that very few headers need to include OS-specific headers (esp. windows.h), as they can be huge.
Be careful, though. On *really* big projects, even with proper ifdefs and/or pragmas, this can cause compile time to creep up and become annoying. You'll know when you've reached that point and need to refactor header use. Organize things so that very few headers need to include OS-specific headers (esp. windows.h), as they can be huge.
A hard requirement for prefessional-quality code is that headers must be idempotent.
That means that the order a header appears in an including file must not matter. The sequelae of that rule is that a header must include all the other headers sufficient to declare names that are referenced, and also must have some form of include guard.
Professional-quality code also dictates that a header file expose no more than is necessary: it should not pull in other headers unless that is required. Minimized compile-time coupling of source units is highly desireable. Prefer forward declarations where possible. Consider the use of headers containing only forward declarations (eg. <iosfwd> in the standard library).
In short: a header shall be idempotent and minimally coupled.
That means that the order a header appears in an including file must not matter. The sequelae of that rule is that a header must include all the other headers sufficient to declare names that are referenced, and also must have some form of include guard.
Professional-quality code also dictates that a header file expose no more than is necessary: it should not pull in other headers unless that is required. Minimized compile-time coupling of source units is highly desireable. Prefer forward declarations where possible. Consider the use of headers containing only forward declarations (eg. <iosfwd> in the standard library).
In short: a header shall be idempotent and minimally coupled.
I worked on a very large project once where we avoided included headers in other headers, supposedly, to speed up compile times.
It was a nightmare.
It was a nightmare.
This topic is closed to new replies.
Advertisement
Popular Topics
Advertisement