Why Do We Need to Use the Header Guards in C++?
While writing your code, you define certain header files on your own, depending upon the functionality you require. After creating these header files, you can include them all in your .cpp file that contains your actual code. However, sometimes these header files depend upon each other. So, you have to include one header file into another. In that case, when you include both these header files into your .cpp file, the same functions of one header file might be defined twice. This leads to the generation of a compile-time error since C++ strictly prohibits the definition of the same function twice within the same code. Therefore, we use the header guards to protect your header files from malfunctioning to resolve this dependency issue.
These header guards can be implemented using the four pre-processor directives: #ifndef, #define, #ifdef, and #endif. For example, whenever you enclose a piece of code within the “#ifndef” directive, the compiler always checks whether the following code has been previously defined or not. If not, then the statements following the “#define” directive are executed. Otherwise, these statements are simply ignored. This, in turn, ensures that your program always compiles successfully and the same functions are not defined more than once within the same code. The “#ifdef” directive works vice-versa. You will be able to understand all this in a better way after going through the following two examples.
Example # 1: Highlighting the Need of the Header Guards in C++
To highlight the importance of header guards in C++, you will have to look through this example. In this instance, we will be creating two header files and one .cpp file. We will also include the first header file in the second header file. After which, we will include both these header files in our .cpp file. Here, we would like to state that whenever a C++ program encounters a duplicate definition of any function, it always generates a compile-time error, such as “your code will not be compiled until you fix that error.” Our first header file is revealed in the following image:
The name of our first header file is “decimal.h”, which refers to the decimal number system which contains numbers from 0 to 9, i.e., a total of ten numbers. In this header file, we have included the “iostream” library and our “std” namespace. This is followed by a function named “getTotal()”, intended to return the total number of the decimal numbers present in the decimal number system.
Our second header file is shown in the following image:
The name of our second header file is “hex.h”, which refers to the hexadecimal number system. This file contains numbers from 0 to 9 and characters from A to F, which is a total of 16 numbers. Since the decimal number system is also a small part of the hexadecimal number system, we have simply included our first header file in our second header file.
Then, our .cpp file is revealed in the image below:
The name of our .cpp file is “main.cpp” since it will primarily contain our driver function. First, we have included the two header files that we have created above and then the “iostream” library. After that, we simply wanted to print out a message on the terminal within our “main()” function to notify the user that the compilation of the code had taken place successfully. This C++ code will look normal to you. However, you will be able to find out the errors in it once you execute it.
When we compiled and executed our .cpp file, the error shown in the following image was generated on our terminal:
We will briefly talk about this error now. In simple words, this error message says that the function “getTotal()” has been defined twice within our code. Now, you might be doubting how this occurred since we only defined this function once. Well, we included the “decimal.h” header file in our “hex.h” header file. Then, when we had both these files in our “main.cpp” file, the same function was defined twice because of the inclusion of one header file into another. Since redefinition of the same function is strictly not allowed in C++, we could not compile our program successfully. This calls for the need to use the header guards in C++.
Example # 2: Using the Header Guards in C++
This example is just a slight modification of our first example with header guards in C++. Our modified “decimal.h” header file is presented in the following image:
In this modified header file, we have used the “ifndef DECIMAL_H” directive at the start, followed by the “define DECIMAL_H” directive. “DECIMAL_H” refers to the name of our header file “decimal.h”. Then, we have our normal code as it is. Finally, we have closed our program with the “endif” directive.
In the same manner, we modified our second header file with the same directives, as shown in the following image:
However, our “main.cpp” file has remained the same since we do not need to modify it as such. Now, when we tried to compile our .cpp file, it did not generate any error message, or in other words, it got compiled successfully, as you can see from the image shown below:
After compiling this program, we executed it. Hence, the message that we wanted to display on the terminal through our “main()” function was displayed on the terminal, as shown in the following image:
This time, our program was executed successfully despite including both the header files in our “main.cpp” file solely because of using the header guards in C++ wherever required.
In this guide, we wanted to discuss the header guards in C++ in Ubuntu 20.04. Initially, we explained what header guards are while emphasizing their need in C++. Then, we thoroughly explained two different examples, such as highlighting the need for header guards and explaining how to use them. Once you understand these examples well, you will quickly realize why it is important to use the header guards while dealing with the header files in C++.