If you are a C++ programmer, then you must have used predefined header files like <iostream>
, <string>
, <cmath>
etc.
In this article, I have shown how to create your own header files in the right way.
This article is divided into two sections -
Creating Ordinary Header Files
Suppose you have created the following header file with the following line in it-
const int SOME_CONSTANT = 50; // first.h
// ...More
Now you have a second header file which has included the above file -
#include "first.h" // second.h
// ...More
And you have one more C++ file which includes both these files -
#include "first.h" // third.cpp
#include "second.h"
// ...More
Now, when you compile your third.cpp
file, you will get an error like this -
error: redefinition of ‘const int SOME_CONSTANT’ 1 | const int SOME_CONSTANT = 50;
Because the first.h
file is included twice, therefore its content also gets copied twice in the third.cpp
file.
To prevent this we should use Header Guards.
Using Header Guards
Header Guards are conditional compilations directives which mean they are evaluated at compilation time and the compiler will perform operations on the basis of its result.
Header Guards look like this -
#ifndef HEADERFILE_H
#define HEADERFILE_h
// Declarations goes here
#endif
ifndef
means if not defined, if the HEADERFILE_H is not defined it will define it, otherwise if it is already defined it will skip compiling everything between ifndef
and endif
.
Now, we will update our previous example with header guards
first.h
#ifndef FIRST_H #define FIRST_H const int SOME_CONSTANT = 50; // ...More #endif
second.h
#ifndef SECOND_H #define SECOND_H #include "first.h" // ...More #endif
third.cpp
#include "first.h" #include "second.h" #include <iostream> int main() { // Using first.h and second.h }
Now, our third.cpp
file will compile without error.
One Complete Example
Let's look at one more example -
In this example, we will create the following three files -
headerfiles.h
- Provides constants, functions and class declarations for use.definitions.cpp
- Contains definitions for the headerfiles.h functions and classes.main.cpp
- Consumes the functions and classes declared in headerfiles.
headerfile.h
#ifndef HEADERFILE_H
#define HEADERFILE_H
#include <iostream>
#include <string>
const int SOME_CONSTANT = 50;
void print(std::string message);
class SomeClass
{
private:
int var;
public:
SomeClass(int var);
void print();
};
#endif
definitions.cpp
#include "headerfile.h"
void print(std::string message)
{
std::cout << "Message = " << message << "\n";
}
SomeClass::SomeClass(int var)
{
this->var = var;
}
void SomeClass::print()
{
std::cout << "var = " << var << "\n";
}
main.cpp
#include "headerfile.h"
#include <iostream>
int main()
{
print("Hello, World"); // OUTPUT: Message = Hello, World
SomeClass object(50);
object.print(); // OUTPUT: var = 50
std::cout << SOME_CONSTANT << "\n"; // OUTPUT: 50
return 0;
}
This is the right way to create header files, you separate the declarations and definitions in two files and only include the header file when necessary.
You will link the definitions file when compiling like this -
g++ -o main main.cpp definitions.cpp
Creating Template Header Files
Now in case of templates, you can't simply create two different files for declarations and definitions like the previous example because, when compilers compile the program it needs to replace the generic types with the required template arguments.
For example -
# foo.h
template <typename type>
type sum(type a, type b)
{
type result = a + b;
return result;
}
# main.cpp
sum(50, 100); // This will create a function sum with type replaced with int everywhere
If we haven't provided the definition in the header file, the compiler won't be able to do perform the replacement(or instantiation).
Now, to separate our definition and declaration in the case of templates we can use the following way -
max.h
Header file to store function and class declaration
#ifndef MAX_H
#define MAX_H
template <typename type>
type max(type a, type b);
#include "max.tpp" // IMPORTANT: We are including our definitions in the header file at the end
#endif
max.tpp
Definition file, note that its extension is .tpp
and not .cpp
as per the convention for template definitions files.
#include "max.h"
template <typename type>
type max(type a, type b)
{
return a > b ? a : b;
}
main.cpp
#include "max.h"
#include <iostream>
int main()
{
std::cout << max(50, 100) << "\n"; // 100
return 0;
}