C++ Language Tutorial

Header and Source Files | C++ Language Tutorial

In this lesson we will talk about some relatively new concepts that I’ve postponed over and over, until this point, where we can actually use them for a better understanding. We will talk about separating your code into separate header and source files.

You will finally see what headers look like and how we can use them to separate different parts of our code into separate source files and header files. In this way we can keep our code more organised, by separating different concepts, which will make it much easier to find what we are looking for when trying to modify our program.

There are also much more upsides of doing this, and we will go through each one of them, but let’s first take a look at what headers really are and look like.

Header files

You’ve been using the help of a header file even from the first program that we wrote in our lessons. If you remember, I’ve explained, in a very general way, what headers are, when we’ve first encountered the #include <iostream> in our programs.

Headers usually have the .h extension and contain declarations that we will use in our source files. Let’s take as an example the iostream header file that we include when working with input-output in our program. We could have never used cout to print to the screen without including the iostream header file because we have never declared and defined that identifier anywhere in our program. That is why we are telling the compiler to include the iostream header file, which actually means that, the compiler will locate and read all the declarations from that header file when it reaches the preprocessor directive, #include.

Usually header files only contain declarations and do not provide the actual definitions. As you’ve seen in the previous lesson, when we only declare a function and do not provide the definition for it, when we are calling that function in our program, the linker will complain about “Unresolved Symbols“. So, how does the compiler know where to get the definition for cout then?

Well, the cout is actually defined in the standard runtime library which is automatically linked in the link process.

What are libraries

Well, simply put, libraries are packages, containers of useful code(functions, objects) with the purpose of being reused in programs. Now, when you are writing a library, you are also writing a header file which contains the declarations of the reusable code that exists in that library and you wish to provide to others to include in their own programs.

When you, or others, wish to use the functionality provided by any library, you actually do not need the entire source code to be included in your projects. You only need the compiled library, (.a, .so, .lib, .dll etc. – depending on the platform you are using) and a header file that you will include in your sources when using any of the functionality it provides. Think of header files just like you think of a table of contents. It is just a very simple container of declarations, so the compiler will know the minimum it needs about the functionality you are using, when it compiles your code.

When using libraries, you do not have to compile the code again, which would be a waste of time, because libraries are always provided as they are, without the need of modifying them. When using a library that is not from the standard runtime, you will also need to let the compiler know which libraries you wish to include, so it knows where to get the symbols from and link them, once they are used in your program.

For now, we will not use separate libraries, but we will get to learn how all of these new concepts work by using separate source files and header files in our project.

Creating your first separate header and source files

For the purpose of this lesson, we shall create separate header and source files that will contain a couple of basic functions that we will use in our main.cpp. Let’s call that source file mathPrimer.cpp and the header file, mathPrimer.h. Let’s create the file mathPrimer.h first, which will contain the following declarations:

mathPrimer.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//
//  mathPrimer.h
//  ChapterII.HeaderSourceFiles
//
//  Created by Vlad Isan on 20/04/2013.
//  Copyright (c) 2013 INNERBYTE SOLUTIONS LTD. All rights reserved.
//
 
#ifndef mathPrimer_h
#define mathPrimer_h
 
int add(int a, int b);
int subtract(int a, int b);
 
#endif

First, let’s take a look a little at the preprocessor directives in this file. You already know what #define is, as we have covered it a little when talking about variables and constants. Let’s talk a little about the conditional compilation#ifdef#ifndef and #endif.

The conditional compilation preprocessor directives, tell your compiler what should be compiled or not and under what conditions.

The #ifdef preprocessor directive basically checks if something was previously defined using the #define preprocessor directive. If this condition is met, then the code between the #ifdef and the corresponding #endif is compiled, otherwise it is ignored by the compiler.

The same goes for #ifndef, and, as you can already imagine, it is the complete opposite of #ifdef, and it allows the compiler to check whether a name has not been defined with #define.

So, why are we using these preprocessor directives in our header file? These preprocessor directives in the header files are called header guards, and helps us to avoid including the same declaration in multiple times.

This works by skipping the entire contents of the header file if it was already included in some other place. In our example, when you first include our header file, mathPrimer.h, the #ifndef condition is met, because we haven’t defined the name mathPrime_h by using the #define preprocessor directive until now. So, the condition is met and everything inside the header file will be compiled, and most importantly, the mathPrimer_h will be defined as well, by using the #define mathPrimer_h right after the #ifndef. Now, when you include the header file a second time, the #ifndef mathPrimer_h condition will not be met, because we have already defined that name when we included the header file in some other place.

By using these header guards we are avoiding the complaint we could get from the compiler when declaring the header contents twice, or even multiple times.

OK, now let’s look at what is declared inside our mathPrimer.h header file. We have two function declarations, add and subtract, both having two integer parameters and an integer return type.

We have to also define our functions. Let’s create another file called mathPrimer.cpp. This file will contain the function definitions:

mathPrimer.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//
//  mathPrimer.cpp
//  ChapterII.HeaderSourceFiles
//
//  Created by Vlad Isan on 20/04/2013.
//  Copyright (c) 2013 INNERBYTE SOLUTIONS LTD. All rights reserved.
//
 
#include "mathPrimer.h"
 
int add(int a, int b) {
    return (a + b);
}
 
int subtract(int a, int b) {
    return (a - b);
}

The first thing we do in this source file is to include our header file, by using the preprocessor directive #include “mathPrimer.h”. Now, when including header files from the standard library, you’ve probably noticed we used angled brackets. The reason is that when including header files that come with the compiler, such as standard library header files, we use angled brackets, but when including header files that we are supplying, we use double quotes, ” “, which tell the compiler to look for the header file, by first searching for it in the current directory where the source files are contained in.

Please note that when we only have declarations in the header files, we do not need to include the header file when defining the functions, as we do in our case. We have to only include it when we are calling the functions declared in it. But, as we already are using header guards, it is a good practice to include it in here as well, because header files can also contain constants which could be used in here as well. As an example, we can declare a constant in our header file to hold the value of PI, and use that constant when defining the other functions, in our mathPrimer.cpp.

As you can notice, we are also defining the functions add and subtract which we are going to use in our main function.

Now, let’s take a look at our main.cpp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//
//  main.cpp
//  ChapterII.HeaderSourceFiles
//
//  Created by Vlad Isan on 20/04/2013.
//  Copyright (c) 2013 INNERBYTE SOLUTIONS LTD. All rights reserved.
//
 
#include <iostream>
#include "mathPrimer.h" /* including our header file */
 
using namespace std;
 
int main()
{
 
    /* calling our functions, add and subtract */
 
    cout << add(10, 2) << endl;
    cout << subtract(10, 2) << endl;
 
    return 0;
}

As you can see we are including our header file in here as well, so we can use the functions declared in it, add and subtract. If you were to remove the #include “mathPrimer.h” from here, you will see that you cannot compile the code, because the compiler would not know anything about those functions, and it will complain about “Undeclared identifiers“.

An important thing to note about header files is to never include variables in them, unless they are constants. Header files should only be used for declarations. Also, you should never include the definition for a function in the header file because it will change the whole scope of having a header file, and it will make it hard to read.

Also, you should always split the parts of your code into separate header and source files grouped by a certain criteria or functionality, because when only needing a part of it, you do not need to include all of the declarations that reside in your program. As in our example, we have called our header file mathPrime.h, as it will only contain basic math functions. If we want to make some other helper functions, for example, for printing to the screen and retrieving user input, we should make another pair of header/source files, that we should only include when needed.

It is always a good practice to keep your code organised, so you should try to follow as many of these advices as you can. These could really save a lot of debugging time, especially when you need to find a particular part of your code that needs to be changed.

4 thoughts on “Header and Source Files | C++ Language Tutorial

Leave a Reply