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.

Using standard library header files

Consider the following program:

This program prints “Hello, world!” to the console using std::cout. However, this program never provide 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 objects, its worth keeping in mind that header files typically only contain function and object declarations, not function and object 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 global constant variables (which we’ll cover in a future lesson).

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 (%Failed lesson reference, id XX%).
  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 given the same name as the code file whose functions the header is providing forward declarations for.

Since our header file will contain a forward declaration for functions defined in add.cpp, we’ll call our new header file add.h.

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 (unchanged):

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

Troubleshooting

If you get a compiler error indicating that add.h was 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. 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 included 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 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 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 stuffix. 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 any more!

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

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). The definitions of all these subsequent header files are also implicitly available for use in your code file.

In your code files, you should never rely on the contents of header files that are included by other header files. 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.

Best practice

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

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

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.
  • Order your #includes as follow: your own user-defined headers first, then 3rd party library headers, then standard library headers.

2.12 -- Header guards
Index
2.10 -- Introduction to the preprocessor

262 comments to 2.11 — Header files

  • gunty

    EgymastKoveto.cpp:

    Fuggvenyek.cpp:

    and fuggvenyek.h:

    When I debug my program, I get these errors:

    1>d:visualc++_workspacenumereconsecutivenumereconsecutivefuggvenyek.cpp(5): error C2065: 'cout' : undeclared identifier
    1>d:visualc++_workspacenumereconsecutivenumereconsecutivefuggvenyek.cpp(7): error C2065: 'cout' : undeclared identifier
    1>d:visualc++_workspacenumereconsecutivenumereconsecutivefuggvenyek.cpp(12): error C2065: 'a' : undeclared identifier
    1>d:visualc++_workspacenumereconsecutivenumereconsecutivefuggvenyek.cpp(12): error C2065: 'szam' : undeclared identifier
    1>d:visualc++_workspacenumereconsecutivenumereconsecutivefuggvenyek.cpp(13): error C2065: 'a' : undeclared identifier
    1>d:visualc++_workspacenumereconsecutivenumereconsecutivefuggvenyek.cpp(13): error C2065: 'a' : undeclared identifier
    1>d:visualc++_workspacenumereconsecutivenumereconsecutivefuggvenyek.cpp(13): error C2065: 'a' : undeclared identifier
    1>d:visualc++_workspacenumereconsecutivenumereconsecutivefuggvenyek.cpp(13): error C2065: 'a' : undeclared identifier
    1>d:visualc++_workspacenumereconsecutivenumereconsecutivefuggvenyek.cpp(14): error C2065: 'a' : undeclared identifier
    1>d:visualc++_workspacenumereconsecutivenumereconsecutivefuggvenyek.cpp(21): error C2065: 'cout' : undeclared identifier
    1>d:visualc++_workspacenumereconsecutivenumereconsecutivefuggvenyek.cpp(22): error C2065: 'cout' : undeclared identifier
    1>d:visualc++_workspacenumereconsecutivenumereconsecutivefuggvenyek.cpp(22): warning C4554: '<<' : check operator precedence for possible error; use parentheses to clarify precedence
    1>d:visualc++_workspacenumereconsecutivenumereconsecutivefuggvenyek.cpp(22): error C2065: 'szam' : undeclared identifier

    If I put both .cpp files in one, the program is working. Why? I don't understand.

    • Alex

      I see the following errors:
      1) Fuggvenyek.cpp uses cout but does not #include iostream.
      2) function keres() uses variables a and szam without defining them (you defined them inside of function main(), which keres() can't see).
      3) You call cout inside of a function instead of std::cout (or add a using statement to each function that uses cout)

  • Unknown

    if we have a project that just need a header for example A.h.
    what will happen if we #include A.h and B.h and the project doesn't even need B.h?

    • Alex

      It will pull all the declarations from B.h into the project anyway, but they won't be used for anything.

      Note that the declarations in B.h could cause naming conflicts with declarations in A.h, if there were any conflicting identifiers.

  • Hi Alex,
    Is there any update library for socket programming in C++ ?
    How I can get it ?

    Thanks.

  • Luis77

    Thank you very much for your answer, Alex.

    I will firstly head for solution #2. Then, I am going to learn about libraries and then switch to solution #3. By the way, is there a chapter on this website about library creation ? (appendix A is only about using libraries not creating them)

  • Luis77

    I would like to make sure I have correctly  understood this chapter, as I am new to C++
    I am using Code::Blocks as C++ IDE.  

    I need to create some mathematical functions that I am going to use in a lot of projects (present and future). e.g.: distance between 2 points, norm of a 3D vector, scalar product, and so on...
    All these functions are related to the mathematical field of geometry. That is why I wrote them in a single *.cpp file called mymathgeom.cpp:

    I have created the corresponding mymathgeom.h file:

    Those 2 files are located in a folder I defined as a general search path in Code::Blocks (not only for one project but for all). The other *.h files I am going to create in the future will be stored in the same folder.

    My first question is: is it a good practice to write several functions to be reused in the same *.cpp file ? Or is it better to write each function in a separate *.cpp file ?

    Then, from each project point of view:

    1) I have to #include "mymathgeom.h" at the beginning of my main.cpp file.
    2) I have to add mymathgeom.cpp to each project.

    Is it right ? What seems a little bit strange to me is point 2): I had to do that in order to build and run my project. But that means that mymathgeom.cpp is compiled every time I compile my project. Is that efficient ? or is there another way to do ?

    Thanks in advance

    • Alex

      Definitely don't write each function in a separate file. That would be an organizational nightmare. :) Best practice is to put related functions into the same .cpp file -- so all of your geometry functions would be a great fit for a geometry.cpp/.h file.

      As far as including your geometry functions, you really have 3 choices:

      1) Copy the files into every project you need them, and add them to your project just like any other project file. They do get recompiled for every project... but using precompiled headers, after you compile the first time they won't get compiled again unless they change. The main downside here is that you're duplicating files, which can make it hard to tell which file is the latest version.

      2) Store all your reusable files in one central location. Add the code files to your project like normal. You can #include the headers like normal, but you'll need to add the location of the headers to your project's include path. If you ever move your central files, this method is a pain.

      3) Precompile your code into a library and tell your compiler to include the library. You can #include the headers like in option 2. This is more of a pain, but makes it a little easier if you want to move where the library lives, as you only have to update the library path in your project settings.

      PS: Definitely check out structs (and later, classes). They're great for handling points (and other geometry concepts, like vectors).

  • raph.ael

    question: i wanna use a *.h for file operations (create/open > updating/reading > close), do i include fstream at main.cpp or can i # in the header file, if im not using fstream for nothing else? can one option or another affect the performance in a good/bad way for build/running a long code?

  • Alex

    #pragma once is an alternative way to do header guards.

    Here's my comment from lesson 1.10 on this topic:

    The problem is that #pragma once isn’t officially a part of the language, and not all compilers support it (even though many modern ones do). Google’s C++ style guide explicitly tells Google developers not to use #pragma once, so we’re following suit here and not recommending its use.

  • Andy

    Hey Alex! :D

    Yesterday I installed Visual Studio Community 2015 (the new version of Visual Studio Express).

    I just wanted to make the program from chapter 2.6 and I thought this would be a nice place to use multiple files and header files. When I created the header file it automaticly had "#pragma once" in it.

    A little googeling tells me that "#pragma once" is like #ifndef, but I'm not sure.

    What do you think about this? :)

  • Typo:
    "If you ever update your directory structure, you('r' is missing here.) code won’t work any more."

Leave a Reply

You can use these HTML tags

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">