標頭檔(header)與include
本文整理自 headers and includes: why and how
Why we need header files
- speeds up compile time
- if everything is in a single file, then everything must be fully recompiled every time you make any little change
- keeps your code more organized
- easier to find the code you are looking for
- allows you to separate interface from implementation
- make the interface visible to other .cpp files, while keeping the implementation in its own .cpp file
Compile Process
- compiler generates intermediate files(object file) for each compiled source file
- compiler will "replace" the #include line with the actual contents of the file you're including when it compiles the file
- files with header extensions might be ignored by the compiler if you try to compile them
- then links all the object files together, which generates the final binary
1 | // in myclass.h |
- Header files should use a .h__ extension (.h / .hpp / .hxx). Which of those you use doesn't matter
- C++ Source files should use a .c__ extention (.cpp / .cxx / .cc). Which of those you use doesn't matter
- C Source files should use .c (.c only)
header files are #included and not compiled, whereas source files are compiled and not #included
The one exception is that it is sometimes (although very rarely) useful to include a source file. This scenario has to do with instantiating templates and is outside the scope of this article
Include guards
include multiple times of the same code would cause error
1 | // myclass.h |
There's an Implicit Example
1 | // x.h |
Because of this scenario, many people are told not to put #include in header files. However this is bad advice and you should not listen to it, But remember
- Only #include things you need to include
- Guard against incidental multiple includes with include guards
- skipping over the entire header if it was already included
1 | //x.h |
The "right way" to include
aware of following dependencies
- stuff that can be forward declared
- stuff that needs to be #included
Dedepency that should be used
- do nothing if
- A makes no references at all to B
- The only reference to B is in a friend declaration
- forward declare B if
- A contains a B pointer or reference
- B* myb, B& myb
- function has B object/pointer/reference as parementer or return type
- B MyFunction(B myb)
- A contains a B pointer or reference
- include "b.h" if
- B is a parent class of A
- A contains a B object
- B myb
1 | myclass.h |
Why that is the "right way" to include
- general idea is that it makes "myclass.h" fully self-contained and doesn't require any other area of the program (other than MyClass's implementation/source file) to know how MyClass works internally
- If some other class needs to use MyClass, it can just #include "myclass.h" and be done with it!
- Alternative method: #include all of MyClass's dependencies before #including "myclass.h"
1 | // I want to use MyClass |
why alternative method is bad:you should fill out all depency header and maintain it's order
1 | // I want to use MyClass |
It is all about encapsulation. Files that want to use MyClass don't need to be aware of what MyClass uses in order for it to work, and don't need to #include any MyClass dependencies. It's all very OO friendly, very easy to use, and very easy to maintain
Circular Dependencies
A circular dependency is when two (or more) classes depend on each other
1 | // a.h |
That's what circular inclusion does
1 | // a.cpp |
Even though you're #including "a.h", the compiler is not seeing A class until B class gets compiled.
Solution: forward declare when you're only using a pointer or reference
Situation below is conceptually impossible(not logical). The solution is to have one or both classes contain a pointer or reference to the other, rather than a full object
1 | // a.h (guarded) |
Function inlining
Inline Function body needs to exist in every cpp file which calls them, otherwise you get linker errors
1 | class B |
The key is that while inline function need to exist in the header, they do not need to exist in the class definition itself
1 | // b.h (assume its guarded) |
even if a.h includes b.h, the additional #includes don't come up until AFTER class B is fully defined, and they are therefore harmless.
But putting function bodies at the end of my header is ugly. Is there a way to avoid that?
1 | // b.h |
1 | // b_inline.h (or b.hpp -- whatever) |
This seperates the interface from the implementation, while still allowing the implementation to be inlined
Forward declaring templates
Forward declaring is pretty straight-forward when it comes to simple classes, but when dealing with template classes, things aren't so simple
1 | // a.h |
Because 'A' isn't really a class, but rather a typedef, the compiler will bark at you
we can't just #include "a.h" here because of a circular dependency problem
We need to forward typedef A
1 | template <typename T> class Tem; // forward declare our template |
A cleaner solution is to create an alternative header which has the forward declarations of your templated classes and their typedefs
1 | //a.h |
This allows B to include a header which forward declares A without including the entire class definition