Programming Workshop 2 (CSCI 1061U)
Faculty of Science, Ontario Tech University
Given a C++ program stored in a file main.cpp
shown
below
#include <iostream>
using namesapce std;
int main(int argc, char** argv)
{
<< “Hello world\n”;
cout }
We create the executable as follows
> g++ main.cpp -o helloworld
It is worth remembering that the above statement first compiles the
file main.cpp
and then links it agains standard libraries.
Afterall we never defined cout
. Did we? Clearly,
cout
is implemented elsewhere.
But what if our program consists of multiple files. Any “interesting” program mostly likely would consist of more than one file. Before we proceed lets convince ourselves that there is benefit to spread the program across multiple files.
Consider, for example, the case when multiple developers work together on a single program. It is easier if each developer has sole access to a file. This prevents one developer overwriting on the work done by another developer. The ability to spread a program across multiple files is extremely useful when multiple developers are working on the same piece of code.
Often times we need to use code written by someone else in our program. How would we do it if we are always restricted to a simple file?
Multi-file programs are everywhere, and we need ways to manage such programs.
Lets consider a simple example. Here we have a program that consists of two files as shown below
main.cpp
#include <iostream>
int main(int argc, char** argv)
{
();
say_greetingsreturn 0;
}
greetings.cpp
#include <iostream>
void say_greetings()
{
std::cout << "Hello world." << std::endl;
}
Lets try our previous approach
g++ main.cpp -o helloworld
.
> g++ main.cpp -o helloworld
main.cpp:5:2: error: use of undeclared identifier 'say_greetings'
say_greetings();
^
1 error generated.
This didn’t work. The linker was not be able to find the
symbol say_greetings
,since it is not declared or defined in
file main.cpp
. How do we then fix this issue? Function
say_greetings
isn’t even defined in this file.
We can fix it by fist compiling the two .cpp files into object files
and then linking the two object files to create the final executable. We
will use -c
switch to tell the g++
that we
only want to compile the C++ files.
First, lets try to compile greetings.cpp
file.
> g++ -c say_greetings.cpp
This will contain a file say_greetings.o
. Do confirm
that such a file is indeed created.
Now lets try to compile main.cpp
> g++ -c main.cpp
.cpp:5:2: error: use of undeclared identifier 'say_greetings'
main();
say_greetings^
1 error generated.
Oops. This doesn’t fix the problem. Is there no end to our troubles?
What we need is to tell the compiler that function
say_greetings
is defined in some other file. An easy way to
do it is to create a header file as follows
say_greetings.h
void say_greetings();
Now we can “include” this file in the main.cpp
file as
follows:
#include <iostream>
#include "say_greetings.h"
int main(int argc, char** argv)
{
();
say_greetingsreturn 0;
}
Now lets try to compile main.cpp
> g++ -c main.cpp
It works now. Confirm that you now have main.o
as
well.
Okay, so now we can link the two object files – main.o
and say_greetings.o
and create the executable.
> g++ main.o say_greetings.o -o helloword
> ./main
Hello world.
It works now! Notice that we started with two files
main.cpp
and say_greetings.cpp
, and we created
a header file say_greetings.h
. We will include this header
file anywhere we need to use the function say_greetings
defined in say_greetings.cpp
file.
In larger programs, with many cpp and h files, it is often preferable to use preprocessor directives to ensure that the header file contents aren’t included twice by mistake. We can do so as follows.
#ifndef __say_greetings_h__
#define __say_greetings_h__
void say_greetings();
#endif
Essentially, when this file is encountered for the first time during
any compiler pass, __say_greetings_h__
is defined. When
this file is encountered the second or subsequent times, its contents
are ignored since __say_greetings_h__
is already
defined.
Consider the following C++ program that comprises 4 files:
The first file contains the main()
function. Each CPP
program must have only one main()
function that serves as
the entry-point into the program.
This file uses Arr
struct two functions
prn_arr()
and ave_arr
, which are not defined
in this file.
Struct Arr
is defined in Arr.h
. We use
#include
to include Arr.h
file into
main.cpp
.
Line extern void prn_arr(struct Arr& a);
declares
that function prn_arr
is defined somewhere else. Notice how
we avoided creating a header file just for the sake of a single
function.
Line extern double ave_arr(struct Arr& a);
declares
that function ave_arr
is defined somewhere else. Notice how
we avoided creating a header file just for the sake of a single
function.
main.cpp
#include <iostream>
#include "Arr.h"
using namespace std;
extern void prn_arr(struct Arr& a);
extern double ave_arr(struct Arr& a);
int main()
{
int i = 0;
;
Arr a// double arr[10];
// int sz;
do {
double tmp;
>> tmp;
cin
if (tmp < 0) {
.sz = i;
abreak;
}
.arr[i] = tmp;
a= i + 1;
i } while(true);
(a);
prn_arr<< ave_arr(a) << endl;
cout
return 0;
Function prn_arr
is defined in this file.
out.cpp
#include <iostream>
#include "Arr.h"
using namespace std;
void prn_arr(struct Arr& a)
{
for (int i=0; i<a.sz; i++) {
<< a.arr[i] << ' ';
cout }
<< endl;
cout }
The second file defines ave_arr
.
math.cpp
#include <iostream>
#include "Arr.h"
using namespace std;
double ave_arr(struct Arr& a)
{
double sum=0.;
for (int i=0; i<a.sz; i++) {
= sum + a.arr[i];
sum }
return sum / (double) a.sz;
}
Struct Arr
is defined in the following header file.
Arr.h
struct Arr
{
double arr[10];
int sz;
};
Use g++
with -c
flag to compile cpp files
to object files as follows:
$ g++ -c main.cpp
$ g++ -c out.cpp
$ g++ -c math.cpp
Now link the object files to create the executable
ar
.
$ g++ main.o out.o math.o -o ar
Now you can execute the program as follows:
$ ./ar
Note that we never compiled Arr.h
file. A header
file is only compiled when encountered during a compile pass for a cpp
file that include this file.