C++ Header Files, Code Separation
Header files should contain class and function declarations.
Source files contain class and function definitions.
It is standard practice (i.e. read easier) to have one declaration per header file and one definition per source file, though for small (read simpler helper) objects you sometimes group them with related more substantial objects.
Example: Class Menu
Menu.h: Contains the Menu declaration.
Menu.cpp: Contains the Menu definition.
The reason header files contain the declarations is so that you can include them from multiple source files and thus each source file has exactly the same definition of each class and function.
Consider it this way:
If you did not have header files then you would need to have the class and/or function declarations (without) definitions in every source file, this means a copy of the same declaration in every file. Thus if you modify a class you need to make the same modification in every file. By the use of a header file you have the declaration in one place and thus only one object to modify.
First, you should not put anything into headers that is not needed to be visible by any other file, other than the one that needs it. Then, let's define something we need below.
Translation Unit
A Translation Unit is the current code being compiled, and all the code included by it, directly or indirectly. One Translation unit translates to one .o / .obj file.
Program
That's all your .o / .obj files linked together into one binary file that can be executed to form a process.
What are the main points of having different translation units?
- Reduce dependencies, so that if you change one method of one class, you don't have to recompile all the code of your program, but only the affected translation unit. An
- Reduce possible name clashes by having translation unit local names, that are not visible by other translation unit when linking them together.
Now, how can you split your code into different translation units? The answer is there is no "so you do it!", but you have to consider it on a case-by-case basis. It's often clear, since you have different classes, which can and should be put in different translation units:
foo.hpp:
/* Only declaration of class foo we define below. Note that a declaration
* is not a definition. But a definition is always also a declaration */
class foo;
/* definition of a class foo. the same class definition can appear
in multiple translation units provided that each definition is the same
basicially, but only once per translation unit. This too is called the
"One Definition Rule" (ODR). */
class foo {
/* declaration of a member function doit */
void doit();
/* definition of an data-member age */
int age;
};
Declare some free functions and objects:
/* if you have translation unit non-local (with so-called extern linkage)
names, you declare them here, so other translation units can include
your file "foo.hpp" and use them. */
void getTheAnswer();
/* to avoid that the following is a definition of a object, you put "extern"
in front of it. */
extern int answerCheat;
foo.cpp:
/* include the header of it */
#include "foo.hpp"
/* definition of the member function doit */
void foo::doit() {
/* ... */
}
/* definition of a translation unit local name. preferred way in c++. */
namespace {
void help() {
/* ... */
}
}
void getTheAnswer() {
/* let's call our helper function */
help();
/* ... */
}
/* define answerCheat. non-const objects are translation unit nonlocal
by default */
int answerCheat = 42;
bar.hpp:
/* so, this is the same as above, just with other classes/files... */
class bar {
public:
bar(); /* constructor */
};
bar.cpp:
/* we need the foo.hpp file, which declares getTheAnswer() */
#include "foo.hpp"
#include "bar.hpp"
bar::bar() {
/* make use of getTheAnswer() */
getTheAnswer();
}
Please note that names within an anonymous namespace (as above) do not clash since they appear to be translation unit local. in reality they are not, they just have unique names so that they do not clash. if you really want (there is little reason to) translation unit local names (for example because of compatibility with c so C code can call your function) you can do it like this:
static void help() {
/* .... */
}
The ODR also says that you cannot have more than one definition of any object or non-inline function in one program (classes are types, not objects, so it doesn't apply to them). So you have to watch out not to put non-inline functions into headers, or not putting objects like "int foo;" in headers. That will cause linker errors then when the linker tries to link the translation units including those headers together.
I hope i could help you a bit. Now that was a long answer, there are indeed errors somewhere. I know that a translation unit strictly is defined another way (output of the pre-processor). But i think it would not add big value to include that into the above, and it would confuse the matter. Please feel free to slap me if you find real bugs :)
Deciding how to separate your code into different classes/functions is one of main tasks of programing. There are many different guidelines on how to do this and I would recommend reading some tutorials on C++ and Object Oriented Design to get you started.
Some basic guidelines will be
- Put things together which are used together
- Create classes for domain objects (eg files, collections etc)
Header files allow you to declare a class or function and then use it in several different source files. For example, if you declare a class in a header file
// A.h
class A
{
public:
int fn();
};
You can then use this class in several source files:
// A.cpp
#include "A.h"
int A::fn() {/* implementation of fn */}
//B.cpp
#include "A.h"
void OtherFunction() {
A a;
a.fn();
}
So header files enable you to separate the declaration from the implementation. If you were to put everything (declaration and implementation) in a source file (eg A.cpp) then try to include that in a second file, eg
// B.cpp
#include "A.cpp" //DON'T do this!
Then you could compile B.cpp but when you try to link your program the linker will complain that you have multiply defined objects - this is because you have multiple copies of the implementation of A.