Separate Compilation: An Example¶
It is quite common to divide medium to large C++ programs into separately
compiled modules. In these notes, we’ll see how to separately compile a class
called RGB_color
(for representing colors).
The General Idea¶
The command g++ -c somefile.cpp
while compile the file
somefile.cpp
and produce an output file called somefile.o
. The .o
indicates that this is an object code file. In general, the input to a
compiler is a source code file, and the output of the compiler is an object
file.
You cannot run object files directly: they contained the results of
compilation, but they are not executable. To create an executable program, you
have to link the object files using a linker (g++ -o
is the linker
we’ll use).
So far we’ve had g++
combine compiling and linking into a single step for
us. But for separate compilation, we will individually compile .cpp
files,
and then link the resulting .o
object files.
To separately compile a class, you have to:
Move all the methods outside of the class so that the class only contains method headers and member variables.
Put the class into a
.h
header file. Usually the header file is the same name as the class, e.g. if your class is namedRGB_color
, you put it inRGB_color.h
.Put the implementations of all the methods into a
.cpp
. If the class is namedRGB_color
, then this file would normally be calledRGB_color.cpp
. Importantly, this.cpp
file does not have amain
function;main
will appear in some other.cpp
file that uses the class.To compile this class use this command:
$ g++ -c RGB_color.cpp // note the -c option
This creates the file
RGB_color.o
.To create an executable file, you need to create one more file, say
color_test.cpp
, that contains themain
function. You compile it like this:$ g++ -c color_test.cpp // note the -c option
This creates the file
color_test.o
.Finally, to create the executable file we link the
.o
files like this:$ g++ -o color_test color_test.o RGB_color.o // note the -o option
g++
with the-o
option calls the linker, and combines the two.o
object files into a single executable file calledcolor_test
. Now you can run it like this:$ ./color_test
All In One File: RGB_color_allInOne.cpp¶
This file contains:
- the
RGB_color
class with the implementations of all methods inside the class; - a couple of non-method functions (
dist
andoperator<<
) defined after the class - a
main
function that testsRGB_color
To run this code, type this command (it assumes you have the course makefile in the same folder):
$ make RGB_color_allInOne
This creates an executable file named RGB_color_allInOne
that run like
this:
$ ./RGB_color_allInOne
// RGB_color_allInOne.cpp
#include "cmpt_error.h"
#include <iostream>
#include <cmath>
using namespace std;
class RGB_color {
int red = 0;
int green = 0;
int blue = 0;
public:
RGB_color() // default constructor
{ } // member initialization is used to set red, green, blue
RGB_color(int r, int g, int b)
: red(r), green(g), blue(b)
{
if (red < 0 || red > 255) cmpt::error("bad red value");
if (green < 0 || green > 255) cmpt::error("bad green value");
if (blue < 0 || blue > 255) cmpt::error("bad blue value");
}
RGB_color(const RGB_color& other)
: RGB_color(other.red, other.green, other.blue)
{ }
int get_red() const { return red; }
int get_green() const { return green; }
int get_blue() const { return blue; }
void invert();
}; // class RGB_color
void RGB_color::invert() {
red = 255 - red;
green = 255 - green;
blue = 255 - blue;
}
ostream& operator<<(ostream& out, const RGB_color& c) {
out << "(" << c.get_red() << ", "
<< c.get_green() << ", "
<< c.get_blue() <<
")";
return out;
}
// returns the distance between two colors
double dist(const RGB_color& a, const RGB_color& b) {
cout << "dist(" << a << ", " << b << ") ...\n";
int dr = a.get_red() - b.get_red();
int dg = a.get_green() - b.get_green();
int db = a.get_blue() - b.get_blue();
return sqrt(dr*dr + dg*dg + db*db);
}
int main() {
RGB_color bg(200, 100, 55);
cout << "bg=" << bg << "\n";
RGB_color fill(55, 155, 200);
cout << "fill=" << fill << "\n";
double d = dist(bg, fill);
cout << "d=" << d << "\n";
}
Methods Outside the Class: RGB_color_split.cpp¶
This file contains:
- the implementations of all the methods of the
RGB_color
class ware put outside the class; only the method headers (and member variables) remain in the class - the names of methods declared outside of the
RGB_color
class must start withRGB_color::
so that C++ knows they are part of theRGB_color
class - a couple of non-method functions (
dist
andoperator<<
) defined after the class - a
main
function that testsRGB_color
To run this code, type this command (it assumes you have the course makefile in the same folder):
$ make RGB_color_split
This creates an executable file named RGB_color_allInOne
that run like
this:
$ ./RGB_color_split
// RGB_color_split.cpp
// This file shows how the implementations of the methods can be written
// outside of the RGB_color class.
#include "cmpt_error.h"
#include <iostream>
#include <cmath>
using namespace std;
class RGB_color {
int red = 0;
int green = 0;
int blue = 0;
public:
RGB_color();
RGB_color(int r, int g, int b);
RGB_color(const RGB_color& other);
int get_red() const;
int get_green() const;
int get_blue() const;
void invert();
}; // class RGB_color
//
// The following are methods defined inside the RGB_color class, and so they all
// start with RGB_color::
//
RGB_color::RGB_color() // default constructor
{ } // member initialization is used to set red, green, blue
RGB_color::RGB_color(int r, int g, int b)
: red(r), green(g), blue(b)
{
if (red < 0 || red > 255) cmpt::error("bad red value");
if (green < 0 || green > 255) cmpt::error("bad green value");
if (blue < 0 || blue > 255) cmpt::error("bad blue value");
}
RGB_color::RGB_color(const RGB_color& other)
: RGB_color(other.red, other.green, other.blue)
{ }
int RGB_color::get_red() const { return red; }
int RGB_color::get_green() const { return green; }
int RGB_color::get_blue() const { return blue; }
void RGB_color::invert() {
red = 255 - red;
green = 255 - green;
blue = 255 - blue;
}
//
// The following are functions (not methods) defined outside RGB_color.
//
ostream& operator<<(ostream& out, const RGB_color& c) {
out << "(" << c.get_red() << ", "
<< c.get_green() << ", "
<< c.get_blue() <<
")";
return out;
}
// returns the distance between two colors
double dist(const RGB_color& a, const RGB_color& b) {
cout << "dist(" << a << ", " << b << ") ...\n";
int dr = a.get_red() - b.get_red();
int dg = a.get_green() - b.get_green();
int db = a.get_blue() - b.get_blue();
return sqrt(dr*dr + dg*dg + db*db);
}
int main() {
RGB_color bg(200, 100, 55);
cout << "bg=" << bg << "\n";
RGB_color fill(55, 155, 200);
cout << "fill=" << fill << "\n";
double d = dist(bg, fill);
cout << "d=" << d << "\n";
}
A Header and an Implementation: RGB_color.h and RGB_color.cpp¶
The file RGB_color.h
contains:
the
RGB_color
class containing just method headers (and member variables), along with just the headers of the two functions,dist
andoperator<<
the file begins and ends with pre-processor commands:
#ifndef RGB_COLOR_H #define RGB_COLOR_H // ... #endif // matches the #ifndef
the C++ pre-processor runs before the compiler, and it is all of its commands begin with
#
(so#include
is a pre-processor command)#ifndef
means “if the following symbol is not defined …”, and so#ifndef RGB_COLOR_H
tests to see if the pre-processor symbolRGB_COLOR_H
has been defined; the first time the#ifndef
command runs,RGB_COLOR_H
will not be defined, but every subsequent time it is run it will be defined (thanks to the#define RGB_COLOR_H
command)the purpose of using
#ifndef
and#include
like this is to permit multiple inclusion ofRGB_color.h
; without this trick, if you happen to #includeRGB_color.h
more than once (something that is very easy to do in big programs) you’ll get a compiler error saying the classRGB_color
has already been definedRGB_color.h
is not compile or linked directly: it is simply #included by any file that usesRGB_color
The file RGB_color.cpp
contains:
- the implementations of all of the
RGB_color
methods - the implementations of the functions
dist
andoperator<<
- importantly,
RGB_color.cpp
does not contain amain
function
RGB_color.cpp
is compiled with this command:
$ g++ -c RGB_color.cpp // note the -c option
This creates a .o
object file named RGB_color.o
. Since there is no
main
function, we can’t create an executable file.
Linking to Create a Test Program: color_test.cpp¶
This file contains:
#include "RGB_color.h"
at the top so that it can use theRGB_color
class- a
main
function with some code for testingRGB_color
RGB_color.cpp
is compiled with this command:
$ g++ -c color_test.cpp // note the -c option
This creates a .o
object file named color_test.o
.
Now, since both color_test.o
and RGB_color.o
have been created, we can
link them together to make the final executable file using this command:
$ g++ -o color_test color_test.o RGB_color.o // note the -o option
This creates the executable file color_test
which can be run like this:
$ ./color_test
// color_test.cpp
#include "RGB_color.h"
#include <iostream>
using namespace std;
int main() {
RGB_color bg(200, 100, 255);
cout << "bg=" << bg << "\n";
RGB_color fill(55, 155, 200);
cout << "fill=" << fill << "\n";
double d = dist(bg, fill);
cout << "d=" << d << "\n";
}
A Note about Makefiles¶
The following commands are needed to create the color_test
executable
file:
g++ -c RGB_color.cpp // compile
g++ -c color_test.cpp // compile
g++ -o color_test color_test.o RGB_color.o // link
In addition, g++
is usually run with many other options which we have
skipped here to make the commands more readable.
The make
program, and makefiles, were designed specifically deal with
issues of compiling and linking code like this. Makefiles essentially provide
a special-purpose scripting language for building C/C++ (and other) programs.
It is somewhat intelligent, and can avoid unnecessarily compiling programs if
it knows that the source code file has not changed.
make
is an older program and the syntax is a bit messy and unusual, so we
will not go into any more details about it here. But keep in mind that big
programs, or any program that uses separate compilation, needs to use
make
, or some tool like it to manage the commands and options for
compiling and linking.