All header files do is tell the compiler that some thing (variable, constant, function, etc) exists somewhere else, just
not in this file.
So, suppose you have a file:
1 2 3 4 5 6 7 8
|
// next.cpp
int the_next_value = 0;
int get_next_value()
{
return the_next_value++;
}
| |
The compiler can build that into code, but in order to use it, other files need to know that the things in it exist. You do that with prototypes and extern declarations.
• A
prototype declares that some function exists somewhere
• An
extern declares that some value (not a function) exists somewhere
1 2 3 4 5 6 7 8 9 10 11 12 13
|
// main.cpp
#include <iostream>
extern int the_next_value; // This exists somewhere (just not here)
int get_next_value(); // This also exists somewhere (just not here)
int main()
{
the_next_value = 7;
std::cout << get_next_value() << "\n";
std::cout << get_next_value() << "\n";
}
| |
Now when the compiler compiles
main.cpp, it also knows what the things in
next.cpp look like. Once both
main.cpp and
next.cpp are compiled, they can be linked together to produce an executable.
C:\Users\Katie\prog> g++ main.cpp next.cpp -o myprog
C:\Users\Katie\prog> myprog
7
8
C:\Users\Katie\prog>
|
Now we run into a potential other problem: every time we want a new cpp file that uses the stuff in
next.cpp we have to repeat all the extern and prototype declarations. Over and over and over. What a headache!
Instead, we write a header file that does that for us:
1 2 3 4 5 6 7 8
|
// next.hpp
#ifndef NEXT_HPP
#define NEXT_HPP
extern int the_next_value;
int get_next_value();
#endif
| |
Now all we need to do is directly
include the contents of that header file in any cpp file we wish.
1 2 3 4 5 6 7 8 9 10 11 12
|
// main.cpp
#include <iostream>
#include "next.hpp"
int main()
{
the_next_value = 7;
std::cout << get_next_value() << "\n";
std::cout << get_next_value() << "\n";
}
| |
This is exactly how the standard include files work. For example, when you
#include <iostream>
, you are getting reference to three objects:
•
std::cin
•
std::cout
•
std::cerr
Just like
the_next_value, these three objects are declared
extern by the
iostream header. The compiler automatically links the cpp file that actually has the standard I/O objects in it to your executable. Using the #include directive lets you use them in any cpp file you wish.
For those of you who know better, this is a very simplified explanation. Don't get hung up on details!
Finally, to answer your actual question:
• A
declaration only tells the compiler that something exists and what it looks like
• A
definition not only declares something, but it gives all the implementation details too.
Hope this helps.