Ready to practice?
Sign up to access interactive coding exercises and track your progress.
Using Header Files
Learn to create and use header files effectively.
Prerequisites
What are Header files?
Header files are C++ files that contain declarations - they tell the compiler about functions, classes, constants, and types without providing the actual implementation. Think of them as a "table of contents" or "menu" that lists what's available in your code library.
Let's learn about why we should use them.
The forward declaration problem
In the previous lessons, we saw how to split programs across multiple files. But there's a significant problem. We need to forward declare functions in every file that uses them.
math_operations.cpp
double add(double a, double b)
{
return a + b;
}
double subtract(double a, double b)
{
return a - b;
}
double multiply(double a, double b)
{
return a * b;
}
calculator.cpp
#include <iostream>
// We need these forward declarations to use the functions
double add(double a, double b);
double subtract(double a, double b);
double multiply(double a, double b);
int main()
{
double result1 = add(5.0, 3.0);
double result2 = subtract(10.0, 4.0);
double result3 = multiply(2.0, 6.0);
std::cout << "Results: " << result1 << ", " << result2 << ", " << result3 << std::endl;
return 0;
}
statistics.cpp
#include <iostream>
// We need the same forward declarations again!
double add(double a, double b);
double subtract(double a, double b);
double multiply(double a, double b);
double calculateMean(double a, double b)
{
return add(a, b) / 2.0;
}
double calculateRange(double a, double b)
{
return subtract(a, b); // Assumes a > b
}
int main()
{
double mean = calculateMean(10.0, 20.0);
std::cout << "Mean: " << mean << std::endl;
return 0;
}
This approach has serious problems:
- Code duplication: Same forward declarations in multiple files
- Maintenance nightmare: If a function signature changes, update every file
- Error-prone: Easy to make mistakes when copying declarations
- Hard to manage: Difficult to keep track of all dependencies
How do we solve this problem?
Header files solve this problem. They are files (typically with .h or .hpp extension) that contain:
- Function declarations (forward declarations)
- Type definitions
- Constants
- Class declarations
- Other shared code that multiple source files need
Creating your first header file
Let's solve our math operations problem with a header file:
math_operations.h (header file)
// math_operations.h - Header file for mathematical operations
#ifndef MATH_OPERATIONS_H
#define MATH_OPERATIONS_H
// Function declarations
double add(double a, double b);
double subtract(double a, double b);
double multiply(double a, double b);
double divide(double a, double b);
// Constants
double PI = 3.14159265359;
double E = 2.71828182846;
#endif // MATH_OPERATIONS_H
math_operations.cpp (implementation file)
// math_operations.cpp - Implementation of mathematical operations
#include "math_operations.h" // Include our own header
double add(double a, double b)
{
return a + b;
}
double subtract(double a, double b)
{
return a - b;
}
double multiply(double a, double b)
{
return a * b;
}
double divide(double a, double b)
{
return a / b; // Caller should check for division by zero
}
main.cpp (using the header)
#include <iostream>
#include "math_operations.h" // Include our header
int main()
{
double x = 10.0, y = 3.0;
std::cout << "x + y = " << add(x, y) << std::endl;
std::cout << "x - y = " << subtract(x, y) << std::endl;
std::cout << "x * y = " << multiply(x, y) << std::endl;
std::cout << "x / y = " << divide(x, y) << std::endl;
std::cout << "Pi = " << PI << std::endl;
std::cout << "e = " << E << std::endl;
return 0;
}
Output:
x + y = 13
x - y = 7
x * y = 30
x / y = 3.33333
Pi = 3.14159
e = 2.71828
How header files are compiled
Here's what happens during compilation when using header files:
┌───────────────────┐ #include "math_operations.h" ┌─────────────────┐ #include <iostream>
│math_operations.cpp│ ───────────────────────────── │ main.cpp │ ─────────────────────┐
└───────────────────┘ │ └─────────────────┘ │
│ ▼ │
│ ┌───────────────────┐ ▼
│ ─────────────── │ math_operations.h │ ┌─────────────────┐
│ │ └───────────────────┘ │ iostream │
│ │ │ │ (header file) │
│ │ ▼ └─────────────────┘
▼ ▼ ┌─────────┐ │
┌─────────┐ │ Compile │ ◄────────────────────────────────────────────────┘
│ Compile │ └─────────┘
└─────────┘ │
│ │
│ ▼
▼ ┌─────────┐
┌───────────────────┐ │ main.o │
│ math_operations.o │ └─────────┘
└───────────────────┘ │
│ │
└─────────────┐ ┌────────┘
│ │
▼ ▼
┌─────────┐ ┌─────────────────┐
│ Link │◄───│ C++ Standard │
└─────────┘ │ Library │
│ └─────────────────┘
▼
┌─────────┐
│main.exe │
└─────────┘
Key points:
- Preprocessing: Each source file that includes a header gets a copy of the header's contents inserted at the
#includelocation - Separate compilation: Each
.cppfile (with its included headers) is compiled independently into an object file (.o) - Linking: All object files are linked together to create the final executable
- Header files are never compiled directly - they're only compiled as part of the source files that include them
This is why header files should only contain declarations, not definitions - otherwise you'd get multiple definitions during linking!
Header guards
Notice the #ifndef, #define, and #endif in the header? These are header guards. They prevent the header from being included multiple times in the same file.
A simple example
Let's create a basic calculator with header files:
calculator.h
#ifndef CALCULATOR_H
#define CALCULATOR_H
// Function declarations
double add(double a, double b);
double subtract(double a, double b);
double multiply(double a, double b);
#endif // CALCULATOR_H
calculator.cpp
#include "calculator.h"
double add(double a, double b)
{
return a + b;
}
double subtract(double a, double b)
{
return a - b;
}
double multiply(double a, double b)
{
return a * b;
}
main.cpp
#include <iostream>
#include "calculator.h"
int main()
{
double x = 10.0;
double y = 3.0;
std::cout << x << " + " << y << " = " << add(x, y) << std::endl;
std::cout << x << " - " << y << " = " << subtract(x, y) << std::endl;
std::cout << x << " * " << y << " = " << multiply(x, y) << std::endl;
return 0;
}
Output:
10 + 3 = 13
10 - 3 = 7
10 * 3 = 30
System headers vs user headers
System headers (angle brackets)
#include <iostream> // Standard library header
#include <string> // Standard library header
#include <vector> // Standard library header
User headers (quotes)
#include "myheader.h" // User-defined header in same directory
#include "utils/math.h" // User-defined header in subdirectory
#include "../common.h" // User-defined header in parent directory
Never include .cpp files with #include.
Including source files may cause naming collisions, longer compile times and it would be an unusual practice.
Real-world header organization
In larger projects, headers are often organized like this:
project/
├── src/
│ ├── main.cpp
│ ├── math/
│ │ ├── arithmetic.h
│ │ ├── arithmetic.cpp
│ │ ├── geometry.h
│ │ └── geometry.cpp
│ ├── ui/
│ │ ├── interface.h
│ │ ├── interface.cpp
│ │ ├── display.h
│ │ └── display.cpp
│ └── utils/
│ ├── string_utils.h
│ ├── string_utils.cpp
│ ├── file_utils.h
│ └── file_utils.cpp
Each directory contains related headers and implementation files.
Benefits of header files
- Organization: Group related declarations together with clear separation of interface and implementation
- Maintainability: Change function signatures once in the header, all files update automatically
- Reusability: Include headers in multiple projects with standard interfaces
- Documentation: Headers serve as clear documentation of available functionality
Summary
Header files are essential for organizing larger C++ programs:
Key concepts:
- Header files: Contain declarations, constants, and shared code
- Implementation files: Contain function definitions
- Include directive:
#includebrings header contents into source files - Header guards: Prevent multiple inclusion problems
Benefits:
- Eliminate code duplication: Declare functions once, use everywhere
- Improve maintainability: Change signatures in one place
- Better organization: Clear separation of interface and implementation
- Enable reusability: Headers can be shared between projects
Best practices:
- Always use header guards
- Include only what you need
- Never use
using namespacein headers - Keep implementations in
.cppfiles - Use meaningful file names and organization
File extensions:
.hor.hppfor headers.cppfor implementation files
Header files are the foundation of modular C++ programming and essential for any project beyond simple single-file programs.
Using Header Files - Quiz
Test your understanding of the lesson.
Practice Exercises
Creating and Using Header Files
Practice creating a simple header file with function declarations. Create a greetings.h file with forward declarations and implement the functions in main.cpp.
Lesson Discussion
Share your thoughts and questions