Separation, by Edvard Munch
It’s often useful to separate the class interface (*.h
)
from the class implementation (*.cc
).
Foo.h | Foo.cc |
---|---|
#ifndef FOO_H_INCLUDED #define FOO_H_INCLUDED #include <iostream> class Foo { public: Foo(); int get_data() const; private: double data; }; std::ostream & operator<<(std::ostream &, const Foo &); #endif /* FOO_H_INCLUDED */ |
#include "Foo.h" using namespace std; Foo::Foo() : data(42.0) { } int Foo::get_data() const { return data; } ostream &operator<<(ostream &os, const Foo &f) { return os << f.get_data(); } |
The *.h
file only contains the method declarations (signatures),
function declarations (for non-member functions & operators) and
declarations of data members. The *.cc
file contains the actual
code for the methods and functions. Neither one contains main
.
*.h
file as containing the interface,
the contract between you, the developer, and the end user.
We can develop the interface long before we have to
actually write the code. It’s easier to consider the merits
of the interface unencumbered by the details of the implementation.
*.cc
.
Foo.h
and Foo.o
to your customers. This keeps your
implementation source code out of your customer’s hands.
“But it’s more typing!”
If you’re afraid of typing, then you have certainly chosen the wrong profession, and the wrong century to live in.
Compile the code like this:
g++ -Wall main.cc Foo.cc
If you do this with a Makefile
, make sure to have main.cc
also
depend upon Foo.h
. After all, if you change Foo.h
(say, by
adding a member variable) then you must recompile main.cc
, because
the size of Foo
has changed.
Do not compile the *.h
file, unless you really know what you’re
doing. That will result in a *.gch
(compiled header) file, which will
cause no end of trouble. Remove it if you ever find it.
Consider this simple program. It gets π from a header file:
% cat main.cc #include "pi.h" #include <iostream> int main() { std::cout << pi << '\n'; return 0; } % cat pi.h constexpr auto pi = 3.14; % g++ -Wall main.cc % ./a.out 3.14 % ls a.out main.cc pi.h
Note that pi.h
is not mentioned in the compile command.
pi.h
?% g++ -Wall main.cc pi.h % ls a.out main.cc pi.h pi.h.gch % ./a.out 3.14Let’s improve the value of π:
% echo "constexpr auto pi = 3.14159;" >pi.h % g++ -Wall main.cc % ./a.out 3.14Hey, why didn’t it work?
% rm pi.h.gch % g++ -Wall main.cc % ./a.out 3.14159
#include "pi.h"
, it first looks for
pi.h.gch
, and uses that precompiled header file if it’s present.
pi.h
, it doesn’t matter,
because the compiler uses pi.h.gch
if it’s there. Ack!
foo.cc
and foo.o
.
Makefile
.
Makefile
so you won’t screw up your compile comand.