Include guards in C++

In this article, you will learn about the include guards in C++ with their examples.

Include guards are commonly used in C++ to restrict the inclusion of the same header file more than once in a single translation unit, often referred to as header guards or macro guards. Duplicate symbol definitions can be avoided this way, which might result in linker errors.

#ifndef checks whether a pre-processor macro named MY_HEADER_FILE_H is not defined. If it's not defined, the code between #ifndef and #endif is included in the translation unit.

#define MY_HEADER_FILE_H defines the macro MY_HEADER_FILE_H. It prevents the code within the #ifndef block from being included if the macro is already defined.

You want to include the header content between the #ifndef and #endif directives. It is the actual content of your header file.

#endif marks the end of the included guard block.

Using this technique, the first time the header file is included in a translation unit, the MY_HEADER_FILE_H macro will not be defined, and the content of the header will be included. On subsequent inclusions within the same or in other translation units, the MY_HEADER_FILE_H macro will already be defined, preventing the content from being included again.

It helps to avoid issues like duplicate definitions and declarations that can occur when a header is included multiple times. It's a common practice to use a macro name based on the header file's name to ensure uniqueness. For example, if your header file is named my_header_file.h, use MY_HEADER_FILE_H as the include guard macro name.

Modern C++ and tools like the C++11 standard introduced the #pragma once directive, which serves the same purpose as include guards. However, #pragma once is not part of the official C++ standard, so if you want to ensure maximum portability and compatibility, use traditional include guards.

Program:

Let's take an example to understand the use of the include guards in C++.

Output:

Dog is of White

Explanation:

  • In this example, the #ifndef ANIMAL_H and #define ANIMAL_H create an include guard to prevent the contents of this header file from being included multiple times in the same translation unit.
  • #include and #include<string> the necessary header files for input/output and string manipulation.
  • The Animal class has three private member variables: name, color, and type.
  • Two public member functions are defined within the class:
  • Void input(): It sets the name and color attributes to specific values, in this case, "Dog" and "White".
  • Void display(): It prints the values of name and color using std::cout.
  • #endif // ANIMAL_H closes the include guard.

Inside the main function:

  • An instance of the Animal class called animal is created.
  • The input() function of the Animal class is called, setting the name and color
  • After that, the display() function of the Animal class is called, which prints "Dog is of White" to the standard output.
  • Return 0 indicates the successful execution of the program.
  • The display generates this output () function's print statement within the main function.

Complexity Analysis:

Time Complexity:

  • The input() and display() member functions of the Animal class both have constant time complexity. They perform a fixed number of operations regardless of the input size.
  • The main() function calls the input() and display() functions, each of which has constant time complexity.
  • Overall, the time complexity of this code is O(1), which means it's constant time complexity.

Space Complexity:

  • The Animal class has three private member variables: name, color, and type. These member variables occupy a constant amount of memory.
  • The Animal object animal is created in the main() function and uses a constant amount of memory.
  • The space complexity of this code is also O(1), which means it uses a constant amount of memory regardless of the input size.
  • In simple terms, the provided code doesn't involve any loops, recursion, or data structures that scale with input size, so both time and space complexities are constant.

Program:

Let's take another example to understand the use of the include guards in C++.

Output:

Sum: 15
Difference: 5

Explanation:

  • In this example, the #ifndef MATH_UTILS_H and #define MATH_UTILS_H lines include guards, which prevent the header from being included more than once in the same translation unit.
  • The two function declarations, int add(int a, int b) and int subtract(int a, int b), are provided without implementations.
  • It includes the h header file, which contains the function declarations.
  • Here, the implementations of the add and subtract functions are provided. They return the sum and difference of the input integers.
  • It includes the iostream standard header for input/output operations.
  • It also includes the h header file, allowing the use of the add and subtract functions.

Inside the main function:

  • In the main() function, two integer variables, x, and y, are declared and assigned values.
  • The add function is called with x and y, and the result is stored in the sum variable.
  • The subtract function is called with x and y, and the result is stored in the difference variable.
  • After that, the results are printed to the console using std::cout.
  • This code demonstrates a modular approach to code organization. The header file h contains function declarations and the implementation file math_utils.cpp provides the actual function implementations.

Complexity Analysis:

Time Complexity:

  • The add and subtract functions in cpp have a constant time complexity, O(1), as they perform a fixed number of operations (addition and subtraction).
  • These functions do not involve loops, recursion, or any operations that scale with input size.
  • The main function in cpp consists of operations, including variable declarations, function calls, and printing.
  • These operations have a constant time complexity, O(1), as they do not scale with input size.
  • The function calls to add and subtract are constant time operations.
  • The entire codebase contains operations with constant time complexity (O(1)) across all functions and files. As a result, the overall time complexity of the entire program remains O(1), or constant time complexity.

Space Complexity:

  • The add and subtract functions do not use additional memory that scales with input size.
  • They only use a constant amount of memory for local variables and parameters.
  • Hence, the space complexity of these functions is O(1).
  • The main function uses a few integer variables (x, y, sum, difference) to store results and temporary values.
  • These variables occupy a constant amount of memory regardless of input size.
  • The memory usage does not grow with the input values.
  • Thus, the space complexity of the main function is O(1).
  • The memory usage across the entire codebase remains constant and does not scale with input size. Therefore, the overall space complexity of the entire program is O(1), or constant space complexity.

Properties:

Include guards in C++ is a technique used to prevent the same header file from being included multiple times in the same translation unit. They help ensure that a header file's contents are only included once, which can prevent issues like duplicate symbol definitions and improve code organization. Here are the key properties of include guards:

Prevent Multiple Inclusions: Include guards of conditional pre-processing directives (#ifndef, #define, #endif) that ensure the header's contents are included only if the associated macro is not defined. It prevents the header from being included multiple times in the same file.

Header File Isolation: You can isolate the declarations and definitions within a header file by including guards. It helps to encapsulate the code and prevents problems arising from multiple inclusions, such as redefinition errors.

Macro Naming Convention: The macro used for the included guard should be unique to the header file. A common convention is using the header file's name in uppercase, with underscores replacing non-alphanumeric characters.

Portability Include guards are a widely accepted practice supported by most C++ compilers. They are portable and help ensure consistent behavior across different platforms and compilers.

Header Dependencies: Include guards also help to manage dependencies between header files. If multiple headers are included, and one depends on the definitions of others, including guards, it ensures that these dependencies are handled correctly.






Latest Courses