Search

2.11 — 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.13 -- 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.12 -- 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.8 -- 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 quotes

You’re probably curious why we use angled brackets for iostream, and double quotes for add.h. The answer is that angled brackets are used to tell the preprocessor that we are including a header file that was included with the compiler, so it should look for that header file in the system directories. The double-quotes tell the preprocessor that this is a user-defined header file we are supplying, so it should look for that header file in the current directory containing our source code first. If it doesn’t find the header file there, it will check any other include paths that you’ve specified as part of your compiler/IDE settings. That failing, it will fall back to checking the system directories.

Rule

Use angled brackets to include header files that come with the compiler. Use double quotes to include any other header files.

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.12 -- Header guards
Index
2.10 -- Introduction to the preprocessor

610 comments to 2.11 — Header files

  • KK

    "However, you should not rely on the content of headers that are included transitively."

    Is this why some people still #include <string> ?? Even though it already have been included in the iostream?

  • KK

    "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!"

    I am sorry but why wouldn't the old programs work anymore??
    What issue did moving the functionality to the using namespace directive present?

  • Math

    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.

    I really can't understand this :( and also if I include my header file in my .cpp file wouldn't that violate the ODR ??

    • nascardriver

      If you don't understand it now, you'll understand it later. Don't worry about it.
      Headers only contain declarations, you can declare things as often as you like without violating the ODR.

  • I don't get "Every header you write should compile on its own (it should #include every dependency it needs)"

    You stated header files consist of forward declaration(s) which will be included by preprocessor into the source file. You earlier mentioned preprocessors don't understand C++. How can the pasted best practice rule then apply? Header files itself will not be compiled if I understand correct.

  • Deep

    Wonderful article. But I have a doubt. I tried the add.h and add.cpp with the main.cpp approach in a Turbo C++ 4.5 compiler and got a linker error in main that undefined symbol add(int,int). I tried everything but it's not working. Then I read the article again and found the troubleshoot tip: "If you get a linker error about function add not being defined, make sure you’ve included add.cpp in your project so the definition for function add can be linked into the program."

    So I included the add.cpp file in main.cpp using #include and the program successfully ran.

    I wasn't happy with this run as I expected that the linker would automatically link add.cpp with main.cpp if I only include add.h in main.cpp. If I have to include the .cpp file then why having the separate .h file with the declaration? Its better not  having the .h file and include the add.cpp file or eliminate the add.cpp and define the function in the .h file itself! But if this would be the case then how the linker links the pre-compiled library file when we only include the header (Like stdio.h or iostream)?

    Then I read the article again and in the last line I found: "Do not #include .cpp files." This contradicts with the troubleshooting point.

    Now I am confused. Please help. I am just a beginner in C++.

    • nascardriver

      "make sure you’ve included add.cpp in your project"
      The use of "included" is misleading in this sentence, I updated it. Never #include cpp files.

      Turbo C++ is no longer maintained, get a new IDE.

      Depending on your IDE, it may or may not automatically compile all .cpp files in your project. Yours doesn't compile add.cpp, so you need to somehow tell turbo c++ to compile it as well. Look up how to add .cpp files in a turbo c++ project.

      • Deep

        Thank you very much sir.. I tried to create a project with both the .cpp files and add them under one project name. My program is running successfully now.

  • Mathari

    Okay so what you are telling me is that I should still #include <string> in my code when it has already been included in the iostream header file ?

  • sami

    Regarding transitive includes, you said, "The implementation of header files may change over time, or be different across different systems.". However, whether we include exclusively or transitively header files, this change would be the same and have the same effect, right? Then why shouldn't we use transitive includes?

    • nascardriver

      The header you're including to get transitive includes from might remove its own includes or not have the same includes on another system.
      For example if your iostream header includes the string header and you don't include the string header even though you use strings, your code will work. Tomorrow there's an update that removes the string include from iostream and now your code is broken.
      You send your code to a friend and his iostream doesn't include string, so your code is broken again.

      If you include everything you use, you don't have this problem.

  • sami

    Hello,

    If it doesn’t find the header file there, it will check any other include paths that you’ve specified as part of your compiler/IDE settings. That failing, it will fall back to checking the system directories.

    Shouldn't "include " be "included"?

  • sami

    Hello,

    "When it comes to functions and objects, it’s worth keeping in mind that header files typically only contain function and object declarations, not function and object definitions."

    What do you mean by "objects" here? Does it mean an instantiation from a use-defined classes?

  • Alek

    HEllo,before you bring up "Header file best practices" I was thinking to myself that we can also treat a .cpp file as a header file and forward delcare our functions there,rather than using .h files.then i saw that you have said "Do not #include .cpp files" why shouldn't we iclude cpp files ?what is the disadvantage of using it this way ?

    • nascardriver

      Hi!

      By the end of chapter 2, you should know why including .cpp files is a bad idea. If you still have question then, please ask again.

      • Alek

        thanks for your answer! I finished this chapter but I didn't see any section talking about why we shouldn't include .cpp files.maybe I haven't red it deeply enough,although I can take this advice and don't do this,I'm a little curious about it.can you please explain it to me briefly here ? however,I found an answer on:https://stackoverflow.com/questions/1686204/why-should-i-not-include-cpp-files-and-instead-use-a-header but it's kind of confusing for me.

        • nascardriver

          If you write code for someone else, they don't care about your definitions, they only need the declarations. The declarations are in the header.
          If you write everything in a source file, they'll be confused by your definitions, because they don't need them.
          They'll be plagued by slow compilation times, because they have to compile the entire source every time they include your source file.
          There'll be multiple definitions of your functions, because your source file gets compiled every time it's included somewhere.

  • Cheeks

    "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."

    Can someone explain this paragraph to me? This is where I'm lost. Why would you need an include in a header if the header is simply function and object declarations. I don't know what object declarations are but how would a function declaration need an include? Aren't function declarations a simply description of function name & input/output types?

    • nascardriver

      We haven't used any types from the standard library yet, or any custom types for that matter, so this isn't easy to understand. To provide an example, I'll use `std::string`, which is a type that can store text.

      cheeks.hpp

      cheeks_broken.cpp

      By including "cheeks.hpp" in "cheeks.cpp", the definitions automatically get access to everything that the declarations need.

      cheeks.cpp

  • Hello dear,
    Wonderful article, I learned a lot of new things from your blog and i used this into my project file and its working.
    Thanks for sharing this great knowledge with us.

  • Mike

    Hi
    Found a typo. In "Q: I didn't include <someheader.h> and my program worked anyway! Why?" near the end of the lesson, "Per the best practice above", should be "per" instead of "Per".

  • Alex

    Small typo:

    "Order your #includes as follow" - "follow" should be "follows"

  • Josh

    Hello, beginner here. I was wondering why not make one header file? Just put all .h files under one file that way you only have to #include one thing? Would this bog down programs? Are there that many headers in certain programs?

    • nascardriver

      That's hard to maintain, slow to compile, and prone to dependency issues. What you're referring to are single-header implementations. They put all of the code (Including definitions) in the header. That makes those libraries very easy to include in a program, but it's not a good solution.

  • Hi,
    Why is it acceptable to use const variable in header files ?
    let's assume we have
    .h file that has a const variables; when we include the header file in both main.cpp file and another cpp file the linker is supposed to throw an error since we have two defination ,ones in main.cpp and once in another cpp file, of const variable while an error is thrown when using a non-const variable.So why we don't get an error in const variable case ?

    • nascardriver

      `const` variables have internal linkage by default, ie. they don't interfere with variables in other files. This is covered in more detail later.

  • Ryan

    Quick typos!
    In the first paragraph- "its worth keeping in mind"; 'its' instead of 'it's'
    In the 'Why doesn’t iostream have a .h extension?' paragraph- "would work any more"; 'any more' instead of 'anymore'
    In 'Headers may include other headers'- "accidentally relying content of a header file"; 'relying content' instead of 'relying on content'

  • Jojo

    Hello,
    For the graph of compilations and linkings of example 2, why don't we have an arrow from add.h to add.cpp (since add.cpp includes add.h)?

  • Tommy Gardner

    Is there any way I can improve this:

    main.cpp

    getValueFromUser.cpp

    void.cpp

    getValueFromUser.h

    Prints:
    Enter two integers:
    (The two
    integers)
    The sum of the integers you entered is: (sum)
    Double the sum is: (sum * 2)

    And what does the #pragma once mean/do at the top of .h files?

  • Kirit

    Hi, I've just come across a small problem if anyone can help me with it. I'm using Visual Studio on Windows 10. I'm up to the section Including header files from other directories, and where it says "Right click on your project in the Solution Explorer, and choose Properties", I having trouble finding the Properties option. I've right clicked on main.cpp, add.cpp, add.h and in the empty space below the files but the right click menu doesn't show an option for Properties. Am I missing something or am I not clicking the right thing?

    • Tommy Gardner

      Im not sure if you're allowed to post links but here:
      https://gyazo.com/11c47e77fbaf6d8ec6ff6aa0b9d33bf6

      https://gyazo.com/cbce059cb2508582f55e1070221c2d66

      If the links are deleted it is at the right side of the screen in the solution explorer.
      Whatever you named your project when it was created that will be the name.

      • Kirit

        Thanks for the reply. I found out what the problem was from looking at the images you posted. My solution explorer window was only showing the files in the project folder and not the options that you had (Project title, References, External Dependencies etc), so I changed it to the sln file with the Switch View option.

  • koe

    The G++ method to include header files from other directories is giving me a big headache.
    1. syntax, should not be a slash at the beginning, slash at end is optional
    g++ -o main -I randofiles/headers/ main.cpp
    2. clang: warning: argument unused during compilation: -I randofiles/headers/
    The -I flag is working as intended and it compiles, but clang seems confused. I have not been able to find a solution to this. Any advice would be appreciated. It will become quite annoying with larger projects.

    UPDATE: using an older version of gcc seemed to fix this, but now I can only use -std=c++11. Woe is me

    • nascardriver

      No space after -I

      I updated the lesson to reflect this.

      • koe

        I get a 'not found' error when using this to compile files

        and no error with

        It may be compiler shenanigans since only the other way works with

        I'll leave this comment here for anyone who might run into problems.

Leave a Comment

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