Search

1.9 — Header files

Headers, and their purpose

As programs grow larger and larger (and include more files), it becomes increasingly tedious to have to forward declare every function you want to use that lives in a different file. Wouldn’t it be nice if you could put all your declarations in one place?

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, sometimes known as an include file. Header files usually have a .h extension, but you will sometimes see them with a .hpp extension or no extension at all. The purpose of a header file is to hold declarations for other files to use.

Using standard library header files

Consider the following program:

This program prints “Hello, world!” to the console using cout. However, this program never defines cout, so how does the compiler know what cout is? The answer is that cout has been declared in a header file called “iostream”. When we use the line #include <iostream>, we’re requesting that all of the content from the header file named “iostream” be copied into the including file. That makes the content from the header file available for use in our code file.

Keep in mind that header files typically only contain declarations. They do not define how something is implemented. So if cout is only declared in the “iostream” header file, where is it actually defined? It is implemented in the C++ runtime support library, which is automatically linked into your program during the link phase.

Consider what would happen if the iostream header did not exist. Wherever you used std::cout, you would have to manually copy in all of the declarations related to std::cout into the top of each file that used it! This would require knowledge of what’s relevant and what’s not. It’s much easier to just #include iostream!

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

We used a forward declaration so that the compiler would know what “add” was when compiling main.cpp. As previously mentioned, writing forward declarations for every function you want to use that lives in another file can get tedious quickly.

Header files can relieve us of this burden. A header file only has to be written once, and it can be included in as many files as needed. This also helps with maintenance by minimizing the number of changes that need to be made if a function prototype ever changes (eg. by adding a new parameter).

Writing our own header files is surprisingly easy. Header files consist of two parts.

The first part is called a header guard, which is discussed in the next lesson (on the preprocessor). Header guards prevent a given header file from being #included more than once in the same file.

The second part is the actual content of the .h file, which should be the declarations for all of the functions we want other files to be able to see. Our header files should all have a .h extension, so we’ll call our new header file add.h:

add.h:

In order to use this header file in main.cpp, we have to include it.

main.cpp that includes add.h:

add.cpp stays the same:

When the compiler compiles 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 function prototype for add(), this prototype is now being used as a forward declaration of add()!

Consequently, our program will compile and link correctly.

Note: When you #include a file, the entire content of the included file is inserted at the point of inclusion.

If you get a compiler error about add.h not being 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”.

If you get a linker error about add() not being defined, make sure you’ve included add.cpp in your project so it compiles properly!

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 compiler 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 compiler that this is a 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.

Header files may include other header files. However, you should never rely on this. Always #include all of the header files you need to use from each .cpp file.

Rule: Each .cpp file should explicitly #include all of the header files it needs to compile.

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, because iostream.h is a different header file than iostream is! To explain requires a very short history lesson.

When C++ was first created, all of the files in the standard runtime library ended in .h. Life was consistent, and it was good. The original version of cout and cin lived in iostream.h. When the language was standardized by the ANSI committee, they decided to move all of the functions in the runtime library into the std namespace (which is generally a good idea). However, this presented a problem: if they moved all the functions into the std namespace, none of the old programs would work any more!

To try to get around this issue and provide backwards compatibility for older programs, 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>.

When you include a header file from the standard library, make sure you use the non .h version if it exists. Otherwise you will be using a deprecated version of the header that is no longer supported.

In addition, many of the libraries inherited from C that were 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.

However, when you write your own header files, you should give them all a .h extension, since you will not be putting your code in the std namespace.

Rule: use the non .h version of a library if it exists, and access the functionality through the std namespace. If the non .h version does not exist, or you are creating your own headers, use the .h version

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:

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 any more.

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.

In Visual Studio, you can 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 your include directories there.

In Code::Blocks, go to the Project menu and select “Build Options”, then the “Search directories” tab. Add your include directories there.

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.

Can I put function or variable definitions in a header file?

You shouldn’t, as it may cause linker errors. We’ll explain why in the upcoming lesson on header guards.

Declarations are okay.

Header file best practices

Here are a few best practices for creating your own header files.

  • Always include header guards.
  • Do not define variables in header files unless they are constants. Header files should generally only be used for declarations.
  • Do not define functions in header files.
  • 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.
  • Give your header files the same name as the source files they’re associated with (e.g. grades.h goes with grades.cpp).
  • Try to minimize the number of other header files you #include in your header files. Only #include what is necessary.
  • Do not #include .cpp files.
1.10 -- A first look at the preprocessor
Index
1.8a -- Naming conflicts and the std namespace

335 comments to 1.9 — Header files

  • jluca013

    I have a a naming issue with Visual Studio 2017. When I create my header file no naming window appears. The IDE defaults to "Header.h". I also do not see a way to rename the file when created. Have you encountered this issue before? Do you know of a solution?

    • jluca013

      I figured out that in order to create and use the header file correctly that it must be done from the "Solution Explorer" window. I am currently trying to understand why header files are useful if they only contain the declarations and not definitions. I am sure I alight will come on once I have contemplated this for a while.

      • Alex

        Header files primarily exist for a few reasons:
        * Propagate forward declarations to multiple files
        * Define types

        That’s not something you’ll do a lot of initially, but the farther you get into C++, the more often you’ll do it. By the time you hit classes, creating multiple files becomes commonplace.

  • i want a small examples for
    Library function, uaerdefinefunction,fundamental function,complex function In c++?

  • Nameless

    I have a quick question. I am using VS 2017 when I am working with the header file I run into an issue with the use of "int" it gives me a
    "PCH WARRNING: Can’t find a suitable stop location. a PCH file was not generated…"
    But when I add #pragma it removes this error all together and I had notice with your example you do not show the use of #pragma if you could possible explain the reason behind this error and the use of #pragma

    when I run with #pragma it doesn’t run but removes this error I had stated above. If I had #include “stdafx.h” everything is fine… so I am a little unclear about this error I have tried googling but I still didn’t really understand this.

    thanks in advance.

    P.S I have been looking for a website that best suits my needs of understanding and you give very clear and precise instructions thank you for this beautiful tutorial. Best C++ tutorial I have ever seen thank you Alex.

    • Alex

      Visual Studio uses precompiled headers by default. If you’re using precompiled headers (and you are, unless you explicitly turn them off) then you should have #include “stdafx.h” as the first line of any code (non-header) file.

  • Sivasankar

    Hi Alex,

    Thanks a lot for your awesome tutorials.

    In "Can I put function definitions in a header file?" section, you said that there will not be any issue if function definitions are put in header file. Won’t it cause linker errors if the same header file is included in multiple files (i.e. the same function is defined in multiple files)?

    • Alex

      Good point. I’ve updated the section to make it more clear that this will probably cause linker errors. I talk about why this happens in the upcoming chapter on header guards.

  • UV

    While i am compiling my add.cpp code i got the following message

    /usr/bin/ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 0 has invalid symbol index 11
    /usr/bin/ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 1 has invalid symbol index 12
    /usr/bin/ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 2 has invalid symbol index 2
    /usr/bin/ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 3 has invalid symbol index 2
    /usr/bin/ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 4 has invalid symbol index 11
    /usr/bin/ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 5 has invalid symbol index 13
    /usr/bin/ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 6 has invalid symbol index 13
    /usr/bin/ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 7 has invalid symbol index 13
    /usr/bin/ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 8 has invalid symbol index 12
    /usr/bin/ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 9 has invalid symbol index 13
    /usr/bin/ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 10 has invalid symbol index 13
    /usr/bin/ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 11 has invalid symbol index 13
    /usr/bin/ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 12 has invalid symbol index 13
    /usr/bin/ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 13 has invalid symbol index 13
    /usr/bin/ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 14 has invalid symbol index 13
    /usr/bin/ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 15 has invalid symbol index 13
    /usr/bin/ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 16 has invalid symbol index 13
    /usr/bin/ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 17 has invalid symbol index 13
    /usr/bin/ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 18 has invalid symbol index 13
    /usr/bin/ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 19 has invalid symbol index 21
    /usr/bin/ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_line): relocation 0 has invalid symbol index 2
    /usr/lib/gcc/x86_64-linux-gnu/4.8/../../../x86_64-linux-gnu/crt1.o: In function `_start’:
    (.text+0x20): undefined reference to `main’
    collect2: error: ld returned 1 exit status

    • Alex

      This is the key error: /usr/lib/gcc/x86_64-linux-gnu/4.8/../../../x86_64-linux-gnu/crt1.o: In function `_start’: (.text+0x20): undefined reference to `main’

      Your program doesn’t have a main() function. You need to create one.

  • AMG

    Many, many thanks for your time and efforts to explain C++ to the rest of the world. Amazing! In this chapter (1.9) identical names for add.h, add.cpp and function ‘add()’ may create a wrong pattern in naming files. For example, Kiril followed "add" naming style and used ‘calc’ to replace ‘add’ from your example. In other words, add.h -> myarithmetic.h, add.cpp-> myarithmetic.cpp. I think it would be better if file names would tell about content. Sorry, if my comment caused wasting your time.

  • Anshul

    Why we don’t need to #include<iostream> in each source code file? add.cpp and main.cpp are compiled independently; to use cout functionality in add.cpp shouldn’t we #include<iostream> here too?

    • Alex

      Yes, you should include iostream in any file that uses the functionality that iostream defines.

      In the example above, add.cpp does not use iostream, so including it is not necessary. If you decided to use std::cout in add.cpp, you’d need to include the iostream header in add.cpp.

  • mrlorin

    I’m really enjoying these tutorials, especially the Douglas Adams tribute?

Leave a Comment

Put C++ code inside [code][/code] tags to use the syntax highlighter