Search

2.10 — Header files

Headers, and their purpose

As programs grow larger (and make use of more files), it becomes increasingly tedious to have to forward declare every function you want to use that is defined in a different file. Wouldn’t it be nice if you could put all your forward declarations in one place and then import them when you need them?

C++ code files (with a .cpp extension) are not the only files commonly seen in C++ programs. The other type of file is called a header file. Header files usually have a .h extension, but you will occasionally see them with a .hpp extension or no extension at all. The primary purpose of a header file is to propagate declarations to code files.

Key insight

Header files allow us to put declarations in one location and then import them wherever we need them. This can save a lot of typing in multi-file programs.

Using standard library header files

Consider the following program:

This program prints “Hello, world!” to the console using std::cout. However, this program never provided a definition or declaration for std::cout, so how does the compiler know what std::cout is?

The answer is that std::cout has been forward declared in the “iostream” header file. When we #include <iostream>, we’re requesting that the preprocessor copy all of the content (including forward declarations for std::cout) from the file named “iostream” into the file doing the #include.

Key insight

When you #include a file, the content of the included file is inserted at the point of inclusion. This provides a useful way to pull in declarations from another file.

Consider what would happen if the iostream header did not exist. Wherever you used std::cout, you would have to manually type or copy in all of the declarations related to std::cout into the top of each file that used std::cout! This would require a lot of knowledge about how std::cout was implemented, and would be a ton of work. Even worse, if a function prototype changed, we’d have to go manually update all of the forward declarations. It’s much easier to just #include iostream!

When it comes to functions and variables, it’s worth keeping in mind that header files typically only contain function and variable declarations, not function and variable definitions (otherwise a violation of the one definition rule could result). std::cout is forward declared in the iostream header, but defined as part of the C++ standard library, which is automatically linked into your program during the linker phase.

Best practice

Header files should generally not contain function and variable definitions, so as not to violate the one definition rule. An exception is made for symbolic constants (which we cover in lesson 4.14 -- Const, constexpr, and symbolic constants).

Writing your own header files

Now let’s go back to the example we were discussing in a previous lesson. When we left off, we had two files, add.cpp and main.cpp, that looked like this:

add.cpp:

main.cpp:

(If you’re recreating this example from scratch, don’t forget to add add.cpp to your project so it gets compiled in).

In this example, we used a forward declaration so that the compiler will know what identifier add is when compiling main.cpp. As previously mentioned, manually adding forward declarations for every function you want to use that lives in another file can get tedious quickly.

Let’s write a header file to relieve us of this burden. Writing a header file is surprisingly easy, as header files only consist of two parts:

  1. A header guard, which we’ll discuss in more detail in the next lesson (2.11 -- Header guards).
  2. The actual content of the header file, which should be the forward declarations for all of the identifiers we want other files to be able to see.

Adding a header file to a project works analogously to adding a source file (covered in lesson 2.7 -- Programs with multiple code files). If using an IDE, go through the same steps and choose “Header” instead of “Source” when asked. If using the command line, just create a new file in your favorite editor.

Best practice

Use a .h suffix when naming your header files.

Header files are often paired with code files, with the header file providing forward declarations for the corresponding code file. Since our header file will contain a forward declaration for functions defined in add.cpp, we’ll call our new header file add.h.

Best practice

If a header file is paired with a code file (e.g. add.h with add.cpp), they should both have the same base name (add).

Here’s our completed header file:

add.h:

In order to use this header file in main.cpp, we have to #include it (using quotes, not angle brackets).

main.cpp:


add.cpp:

When the preprocessor processes the #include "add.h" line, it copies the contents of add.h into the current file at that point. Because our add.h contains a forward declaration for function add, that forward declaration will be copied into main.cpp. The end result is a program that is functionally the same as the one where we manually added the forward declaration at the top of main.cpp.

Consequently, our program will compile and link correctly.

Including a header in the corresponding source file

You’ll see that most source files include their corresponding header, even if they don’t need it. Why is that?

Including the header in the source file increases forward compatibility. It’s very likely that in the future, you’ll add more functions or modify existing ones in a way that they need to know about the existence of each other.

Once we get more into the standard library, you’ll be including many library headers. If you need an include in a header, you probably need it for a function declaration. This means that you’ll also need the same include in the source file. This would lead to you having a copy of your header’s includes in your source file. By including your header in your source file, the source file has access to everything the header had access to.

In library development, including your header in your source file can even help to catch errors early. For an example, see this comment.

Best practice

When writing a source file, include the corresponding header (If one exists), even if you don’t need it yet.

Troubleshooting

If you get a compiler error indicating that add.h isn’t found, make sure the file is really named add.h. Depending on how you created and named it, it’s possible the file could have been named something like add (no extension) or add.h.txt or add.hpp. Also make sure it’s sitting in the same directory as the rest of your code files.

If you get a linker error about function add not being defined, make sure you’ve added add.cpp in your project so the definition for function add can be linked into the program.

Angled brackets vs double quotes

You’re probably curious why we use angled brackets for iostream, and double quotes for add.h. It’s possible that a header file with the same filename might exist in multiple directories. Our use of angled brackets vs double quotes helps give the compiler a clue as to where it should look for header files.

When we use angled brackets, we’re telling the preprocessor that this is a header file we didn’t write ourselves. The compiler will search for the header only in the directories specified by the include directories. The include directories are configured as part of your project/IDE settings/compiler settings, and typically default to the directories containing the header files that come with your compiler and/or OS. The compiler will not search for the header file in your project’s source code directory.

When we use double-quotes, we’re telling the preprocessor that this is a header file that we wrote. The compiler will first search for the header file in the current directory. If it can’t find a matching header there, it will then search the include directories.

Rule

Use double quotes to include header files that you’ve written or are expected to be found in the current directory. Use angled brackets to include headers that come with your compiler, OS, or third-party libraries you’ve installed elsewhere on your system.

Why doesn’t iostream have a .h extension?

Another commonly asked question is “why doesn’t iostream (or any of the other standard library header files) have a .h extension?”. The answer is that iostream.h is a different header file than iostream! To explain requires a short history lesson.

When C++ was first created, all of the files in the standard runtime library ended in a .h suffix. Life was consistent, and it was good. The original version of cout and cin were declared in iostream.h. When the language was standardized by the ANSI committee, they decided to move all of the functionality in the standard library into the std namespace to help avoid naming conflicts with user-defined identifiers. However, this presented a problem: if they moved all the functionality into the std namespace, none of the old programs (that included iostream.h) would work anymore!

To work around this issue, a new set of header files was introduced that use the same names but lack the .h extension. These new header files have all their functionality inside the std namespace. This way, older programs that include #include <iostream.h> do not need to be rewritten, and newer programs can #include <iostream>.

In addition, many of the libraries inherited from C that are still useful in C++ were given a c prefix (e.g. stdlib.h became cstdlib). The functionality from these libraries was also moved into the std namespace to help avoid naming collisions.

Best practice

When including a header file from the standard library, use the no extension version (without the .h) if it exists. User-defined headers should still use a .h extension.

Including header files from other directories

Another common question involves how to include header files from other directories.

One (bad) way to do this is to include a relative path to the header file you want to include as part of the #include line. For example:

While this will compile (assuming the files exist in those relative directories), the downside of this approach is that it requires you to reflect your directory structure in your code. If you ever update your directory structure, your code won’t work anymore.

A better method is to tell your compiler or IDE that you have a bunch of header files in some other location, so that it will look there when it can’t find them in the current directory. This can generally be done by setting an include path or search directory in your IDE project settings.

For Visual Studio users

Right click on your project in the Solution Explorer, and choose Properties, then the VC++ Directories tab. From here, you will see a line called Include Directories. Add the directories you’d like the compiler to search for additional headers there.

For Code::Blocks users

In Code::Blocks, go to the Project menu and select Build Options, then the Search directories tab. Add the directories you’d like the compiler to search for additional headers there.

For GCC/G++ users

Using g++, you can use the -I option to specify an alternate include directory.

The nice thing about this approach is that if you ever change your directory structure, you only have to change a single compiler or IDE setting instead of every code file.

Headers may include other headers

It’s common that a header file will need a declaration or definition that lives in a different header file. Because of this, header files will often #include other header files.

When your code file #includes the first header file, you’ll also get any other header files that the first header file includes (and any header files those include, and so on). These additional header files are sometimes called “transitive includes”, as they’re included implicitly rather than explicitly.

The content of these transitive includes are available for use in your code file. However, you should not rely on the content of headers that are included transitively. The implementation of header files may change over time, or be different across different systems. If that happens, your code may only compile on certain systems, or may compile now but not in the future. This is easily avoided by explicitly including all of the header files the content of your code file requires.

Best practice

Each file should explicitly #include all of the header files it needs to compile. Do not rely on headers included transitively from other headers.

Unfortunately, there is no easy way to detect when your code file is accidentally relying on content of a header file that has been included by another header file.

Q: I didn't include <someheader.h> and my program worked anyway! Why?

This is one of the most commonly asked questions on this site. The answer is, it’s likely working, because you included some other header (e.g. <iostream>), which itself included <someheader.h>. Although your program will compile, per the best practice above, you should not rely on this. What compiles for you might not compile on a friend’s machine.

The #include order of header files

If your header files are written properly and #include everything they need, the order of inclusion shouldn’t matter. However, including your header files in a certain order can help surface mistakes where your header files may not include everything they need.

Best practice

Order your #includes as follows: your own user-defined headers first, then 3rd party library headers, then standard library headers, with the headers in each section sorted alphabetically.

That way, if one of your user-defined headers is missing an #include for a 3rd party library or standard library header, it’s more likely to cause a compile error so you can fix it.

Header file best practices

Here are a few more recommendations for creating and using header files.

  • Always include header guards (we’ll cover these next lesson).
  • Do not define variables and functions in header files (global constants are an exception -- we’ll cover these later)
  • Give your header files the same name as the source files they’re associated with (e.g. grades.h is paired with grades.cpp).
  • Each header file should have a specific job, and be as independent as possible. For example, you might put all your declarations related to functionality A in A.h and all your declarations related to functionality B in B.h. That way if you only care about A later, you can just include A.h and not get any of the stuff related to B.
  • Be mindful of which headers you need to explicitly include for the functionality that you are using in your code files
  • Every header you write should compile on its own (it should #include every dependency it needs)
  • Only #include what you need (don’t include everything just because you can).
  • Do not #include .cpp files.

2.11 -- Header guards
Index
2.9 -- Introduction to the preprocessor

670 comments to 2.10 — Header files

  • Mal

    Thanks that makes sense - it was me being thick :)

  • Mal Jones

    I'm feeling very thick here.  Taking the "add.cpp" example, what we originaly had was a single forward declaration in "int main()". What we have replaced it with is a separate header file add.h that now contains the forward declaration and we now have to replace the forward declaration in "int main()" with an #include "add.h" command, which does not appear to be more efficient.  If you'd had a header file called allFowardDeclarations.h which contained all the forward declarations I could see the point.  What am I missing?

    • Alex

      allForwardDeclarations.h would be huge, and would require recompiling your entire project anytime there was a change to it.

      In C++, it's very common to have code and header file pairs (e.g. add.cpp/add.h), where the header has all of the forward declarations for the corresponding code file. This allows us to create little modules that can be included if only if/when they are needed. It also allows us to more easily see what capabilities another code file is using. In your case, we'd have no idea what capabilities from allForwardDeclarations.h were actually being used. With individual headers, we can see that main.cpp is using capabilities from add, but not from some other header that isn't included.

  • James C

    Is there a way I can get a warning for transitively included headers? For example, if I include <iostream>, it allows me to use std::string, so occasionally, I'll forget to include <string> or whatever other header is being transitively included.

    • Alex

      I'm not aware of any reasonable solution to this problem. The move to modules should eventually address this.

    • nascardriver

      https://include-what-you-use.org/

      iwyu sometimes complains when you don't want it to, and it might fight other tools. There's probably some tinkering required outside of C++ to get it to work the way you want. I've never used it in a project.

  • Halah

    Hello I have problem regarding the header

    "Arduino: 1.9.0-beta (Windows 10), Board: "Arduino Uno"

    libraries\PCM\PCM.c.o (symbol from plugin): In function `startPlayback':

    (.text+0x0): multiple definition of `__vector_11'

    libraries\Servo\avr\Servo.cpp.o (symbol from plugin):(.text+0x0): first defined here

    collect2.exe: error: ld returned 1 exit status

    exit status 1
    Error compiling for board Arduino Uno.
    "

    the header in the code [#include <Servo.h>
    #include <PCM.h>
    Servo Myservo;
    const int trigPin = 8;
    const int echoPin = 7;
    int pos=0;

    const unsigned char sample[] PROGMEM = {
    126, 126, 126, 126, 127, 127, 127, 127, 127, 127, 128, 128};]

    what I supposed to do? I have final project :'''''(

  • Nader Elsarrag

    You said that header files should not include any definitions, just declarations. If it only has declarations, then what header files would you need to include in a header file? I'm not sure if this question makes sense, but could you give me an example of why someone might want to #include "file.h" in another header.h file? If each of these only contains declarations, then why wouldn't the header.h file work without the file.h?

    Please advice <3

  • Shwift

    after copying and trying to compile the second example about writing my own header files, it says:

    _main already defined in Main.obj

    i know where Main.obj is, but i'm not sure if it's best to rename my project, rename Main.obj or just delete Main.obj
    also not really sure what objects are yet.

    • Alex

      It sounds like you might have main() functions defined in two different .cpp files, and the linker is complaining when it merges all the files together?

  • joker

    And why should i name my file with .h or what is .h..
    :)

    • Alex

      Using a .h extension on your header files is common convention. If you do otherwise, it will probably work, but it would be confusing to other people (or maybe your future self).

  • joker

    I don't understand why i got linker error every time i copile the program. I type (#include then my file) but still get linker error. Can someone explin please.
    #include "Cal.cpp" (IN main.cpp)
    Then type in some code and execute it: I get linker error

  • Hi

    Would the <string> library count as a "transitive include" of the <iostream> library?

    • nascardriver

      cppreference lists includes of <iostream> https://en.cppreference.com/w/cpp/header/iostream
      If you want to use `std::string`, include <string>

  • Win

    #include <iostream>
    #include "add.h"
    #include "sub.h"
    #include "mul.h"
    #include "div.h"

    int getValueFromUser() {
        int num{};
        std::cout << "Enter value: ";
        std::cin >> num;
        return num;
    }
    char getOperator() {
        char c;
        std::cout << "Enter an operator: ";
        std::cin >> c;
        return c;
    }

    int main()
    {
        
        int x { getValueFromUser() }; //first value get from user
        int y { getValueFromUser() }; //second value get from user
        

        char z{ getOperator() };
        if (z == '+') {
           std::cout << "the sum of two value is : " << add(x, y) << "\n";
        }
        else if (z == '-') {
           std::cout << "the subract of two value is : " << sub(x, y) << "\n";
        }
        else if (z == '*') {
           std::cout << "the multiplication of two value is : " << mul(x, y) << "\n";
        }
        else if (z == '/') {
           std::cout << "the division of two value is : " << divide(x, y) << "\n";
        }
        else {
           std::cout << "You enter an invalid operator!!" << "\n";
        }
    }

    //Thank for tutorial and this code is an easy version of calculator

    • Heaven

      Thank you for posting this calculator is was a good practice to re-create it without looking at it or copy/pasting!

      Suggestion (I'm a total beginner so maybe my suggestions are irrealistic at this stage)
      1- At the "Else", the program simply end if someone enter an invalid operator. It would be interesting to find a way to send back the user to "getOperator" and make the program run again?

      2- At the end of the program, there could be a std::cout/cin asking the user if he wants to perform another operation (cin yes/no)

      Those are just suggestion, i'm sure you are better at coding compare to me!
      Good luck in your learning :)

    • Ari

      You Could have just put all of them in one file called calculate.h

    • chris

      As someone said earlier, you should only create a header file (with its respective source file) containing the four functions you wrote. Also, the if else chain can be replaced by a 'switch' statement which will provide the same functionality and look a little cleaner in your code.

  • SuperNoob

    I have a very simple question. Maybe I haven't understood the concept correctly. But,
        if cin and cout are forward declared in iostream, then where are the actual definitions?

  • Storm

    Right now, I have 2 separate projects.

    (too lazy to type it all)...\Documents\Winter_Break_2020\C++\ThisProjectDoesNOTHAVEADD
    (too lazy to type it all)...\Documents\Winter_Break_2020\C++\ThisProjectHasTheAdd

    In the first project, I have made a header file that has the line
    int add(int x, int y);

    In the first project, that is "ThisProjectDoesNOTHAVEADD",I have made a program main that calls on add.

    This project also has add.cpp that contains

    Right now, it should NOT run since I commented out the forward declaration. However, I did the whole
    Properties -> VC++ Directories -> Include Directories -> Edit and added a new line with
    (...)\Documents\Winter_Break_2020\C++\ThisProjectHasTheAdd\ThisProjectHasTheAdd

    However, the project "ThisProjectDoesNOTHAVEADD" is still broken. It won't be able to pick up the header file from the other project. Am I doing something wrong? Any guidance would be much appreciated! ^-^

  • Hey Everybody,

    BIG HEADS UP!

    In Virtual Studio 2019... The method for adding an item is a little different than the examples provided in this book.

    By this point, I was getting compiler errors using the "Windows Desktop Wizard" preset for the project.

    Also, the default location my projects were being saved, "C:\Users\User\source\repos", was creating compiler errors because my computer has weird permissions.  

    This was demotivating until I realized it was not my fault and found the solution.

    At this point, I began saving my project in "My Documents". I also began starting all of my projects with the "Empty Project" project option. All of these file-related compiling issues went away after doing this.

    Once you're in the "Empty Project", go to Project > Add item > Create .cpp or .h file.

    It will add the files to the correct location and you won't have any weird file-related compiling issues.

    Hope this helps some people working with Virtual Studio 2019! :)

    • RkzPriest

      Hola que tal como te va, mira yo soy nuevo en el tema de la programacion y me es todo muy verde a la hora de aprender algo... yo tengo un problema similar que el tuyo solo que yo uso visutal studio code y no sabria como es el tema de anidar elementos a mi proyecto. por ejemplo yo ahora segui con el tutorial este pero no pude lograr agregar funciones de cabecera en mis proyectos y queria saber si te manejabas con el vs code como para poder darme una mano ya que yo sigo adelantando pero hay una parte muy importante que es esta que no estaria logrando comprender!! Muchas gracias saludos!

  • J34NP3T3R

    - Every header you write should compile on its own (it should #include every dependency it needs)

    if i understand this correctly, this means that if main.cpp includes add.h and add.h includes subtract.h then i must also include subtract.h in main.cpp ?

    main.cpp

       #include "add.h"
       #include "subtract.h"

    add.h
       #include "subtract.h"

    subtract.h

    if this is correct, my question is

    1. would this violate the Only #include what you need (don’t include everything just because you can). rule ?

    2. wouldn't this bloat the size of our file because we added the contents of subtract.h in main.cpp and add.h ?

    thanks

    • nascardriver

      Only include "subtract.h" in "main.cpp" if you need it

      • J34NP3T3R

        so if i need subtract.h in main.cpp and add.cpp uses subtract.h should i add #include "subtract.h" in main.cpp  
        or does adding    #include "add.h" in main.cpp enough since it includes subtract.h ?

        • nascardriver

          If "add.cpp" needs "subtract.h", then "add.cpp" has to include "subtract.h". Source files don't know about each other, so includes in one source file don't affect another source file. What "add.cpp" includes is irrelevant when you decide what "main.cpp" needs to include. "main.cpp" only has to include what "main.cpp" needs.
          If "add.h" needs "subtract.h" (I doubt it does), then "add.h" has to include "subtract.h". "main.cpp" only includes "subtract.h" if "main.cpp" itself needs something from "subtract.h".

        • Invocator

          Each file should explicitly #include all of the header files it needs to compile, so yes.

  • René De Ridder

    Under best practices you mention "Every header you write should compile on its own". I'm confused about this statement as I thought that only the .cpp files were compiled, not the .h files. Can you clarify?

    • nascardriver

      Headers get compiled as part of every source file that includes them. What this bullet point means it that you have a source file which includes your header and is otherwise empty, the source file should compile.

      • miroslav70

        So, for testing can we take empty main.cpp file, with included tested .H file and .cpp in project? If compile pass, files ok.

        main.cpp

        io.cpp (tested file)

        io.h (tested header)

        • nascardriver

          Yes you can test it like that. You're not testing "io.cpp", "io.cpp" gets compiled independent of "main.cpp", you're only testing "io.h". It doesn't matter which source file you use to test the header. If you already have something in "main.cpp" and don't want to remove it, you can simply create a new .cpp file to include the header.

        • Frans Dumbass Prananto

          Hello Alex and Nascardriver
          This is probably quite dumb question, but is it actually possible for two or more header files to "include" each other?

          Thanks before

          • nascardriver

            You can have mutual `#include`s, but the headers won't be able to use each other's contents.

            1) A includes B
            2) B includes A

            In (2), the include to A has no effect, because A's header guard is already active. B won't see the contents of A. Without the header guard, you'd have an infinite include loop and wouldn't be able to compile.

            When you see 2 files including each other, that's a bug. I can't think of a case where it would make sense.

Leave a Comment

Put all code inside code tags: [code]your code here[/code]