How to Create Header Files in C++

Subscribe to my newsletter and never miss my upcoming articles

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 -

  1. Creating Ordinary Header Files
  2. Creating Template Header Files

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;
}

Thanks for reading


References:

You may also like:

No Comments Yet