[c++] Why have header files and .cpp files?

Why does C++ have header files and .cpp files?

This question is related to c++ header-files

The answer is


C++ compilation

A compilation in C++ is done in 2 major phases:

  1. The first is the compilation of "source" text files into binary "object" files: The CPP file is the compiled file and is compiled without any knowledge about the other CPP files (or even libraries), unless fed to it through raw declaration or header inclusion. The CPP file is usually compiled into a .OBJ or a .O "object" file.

  2. The second is the linking together of all the "object" files, and thus, the creation of the final binary file (either a library or an executable).

Where does the HPP fit in all this process?

A poor lonesome CPP file...

The compilation of each CPP file is independent from all other CPP files, which means that if A.CPP needs a symbol defined in B.CPP, like:

// A.CPP
void doSomething()
{
   doSomethingElse(); // Defined in B.CPP
}

// B.CPP
void doSomethingElse()
{
   // Etc.
}

It won't compile because A.CPP has no way to know "doSomethingElse" exists... Unless there is a declaration in A.CPP, like:

// A.CPP
void doSomethingElse() ; // From B.CPP

void doSomething()
{
   doSomethingElse() ; // Defined in B.CPP
}

Then, if you have C.CPP which uses the same symbol, you then copy/paste the declaration...

COPY/PASTE ALERT!

Yes, there is a problem. Copy/pastes are dangerous, and difficult to maintain. Which means that it would be cool if we had some way to NOT copy/paste, and still declare the symbol... How can we do it? By the include of some text file, which is commonly suffixed by .h, .hxx, .h++ or, my preferred for C++ files, .hpp:

// B.HPP (here, we decided to declare every symbol defined in B.CPP)
void doSomethingElse() ;

// A.CPP
#include "B.HPP"

void doSomething()
{
   doSomethingElse() ; // Defined in B.CPP
}

// B.CPP
#include "B.HPP"

void doSomethingElse()
{
   // Etc.
}

// C.CPP
#include "B.HPP"

void doSomethingAgain()
{
   doSomethingElse() ; // Defined in B.CPP
}

How does include work?

Including a file will, in essence, parse and then copy-paste its content in the CPP file.

For example, in the following code, with the A.HPP header:

// A.HPP
void someFunction();
void someOtherFunction();

... the source B.CPP:

// B.CPP
#include "A.HPP"

void doSomething()
{
   // Etc.
}

... will become after inclusion:

// B.CPP
void someFunction();
void someOtherFunction();

void doSomething()
{
   // Etc.
}

One small thing - why include B.HPP in B.CPP?

In the current case, this is not needed, and B.HPP has the doSomethingElse function declaration, and B.CPP has the doSomethingElse function definition (which is, by itself a declaration). But in a more general case, where B.HPP is used for declarations (and inline code), there could be no corresponding definition (for example, enums, plain structs, etc.), so the include could be needed if B.CPP uses those declaration from B.HPP. All in all, it is "good taste" for a source to include by default its header.

Conclusion

The header file is thus necessary, because the C++ compiler is unable to search for symbol declarations alone, and thus, you must help it by including those declarations.

One last word: You should put header guards around the content of your HPP files, to be sure multiple inclusions won't break anything, but all in all, I believe the main reason for existence of HPP files is explained above.

#ifndef B_HPP_
#define B_HPP_

// The declarations in the B.hpp file

#endif // B_HPP_

or even simpler (although not standard)

#pragma once

// The declarations in the B.hpp file

Because in C++, the final executable code does not carry any symbol information, it's more or less pure machine code.

Thus, you need a way to describe the interface of a piece of code, that is separate from the code itself. This description is in the header file.


Often you will want to have a definition of an interface without having to ship the entire code. For example, if you have a shared library, you would ship a header file with it which defines all the functions and symbols used in the shared library. Without header files, you would need to ship the source.

Within a single project, header files are used, IMHO, for at least two purposes:

  • Clarity, that is, by keeping the interfaces separate from the implementation, it is easier to read the code
  • Compile time. By using only the interface where possible, instead of the full implementation, the compile time can be reduced because the compiler can simply make a reference to the interface instead of having to parse the actual code (which, idealy, would only need to be done a single time).

Responding to MadKeithV's answer,

This reduces dependencies so that code that uses the header doesn't necessarily need to know all the details of the implementation and any other classes/headers needed only for that. This will reduce compilation times, and also the amount of recompilation needed when something in the implementation changes.

Another reason is that a header gives a unique id to each class.

So if we have something like

class A {..};
class B : public A {...};

class C {
    include A.cpp;
    include B.cpp;
    .....
};

We will have errors, when we try to build the project, since A is part of B, with headers we would avoid this kind of headache...


Because C++ inherited them from C. Unfortunately.


Because C, where the concept originated, is 30 years old, and back then, it was the only viable way to link together code from multiple files.

Today, it's an awful hack which totally destroys compilation time in C++, causes countless needless dependencies (because class definitions in a header file expose too much information about the implementation), and so on.


It's the preprocessor way of declaring interfaces. You put the interface (method declarations) into the header file, and the implementation into the cpp. Applications using your library only need to know the interface, which they can access through #include.


Because in C++, the final executable code does not carry any symbol information, it's more or less pure machine code.

Thus, you need a way to describe the interface of a piece of code, that is separate from the code itself. This description is in the header file.


Because C, where the concept originated, is 30 years old, and back then, it was the only viable way to link together code from multiple files.

Today, it's an awful hack which totally destroys compilation time in C++, causes countless needless dependencies (because class definitions in a header file expose too much information about the implementation), and so on.


Because the people who designed the library format didn't want to "waste" space for rarely used information like C preprocessor macros and function declarations.

Since you need that info to tell your compiler "this function is available later when the linker is doing its job", they had to come up with a second file where this shared information could be stored.

Most languages after C/C++ store this information in the output (Java bytecode, for example) or they don't use a precompiled format at all, get always distributed in source form and compile on the fly (Python, Perl).


Often you will want to have a definition of an interface without having to ship the entire code. For example, if you have a shared library, you would ship a header file with it which defines all the functions and symbols used in the shared library. Without header files, you would need to ship the source.

Within a single project, header files are used, IMHO, for at least two purposes:

  • Clarity, that is, by keeping the interfaces separate from the implementation, it is easier to read the code
  • Compile time. By using only the interface where possible, instead of the full implementation, the compile time can be reduced because the compiler can simply make a reference to the interface instead of having to parse the actual code (which, idealy, would only need to be done a single time).

C++ compilation

A compilation in C++ is done in 2 major phases:

  1. The first is the compilation of "source" text files into binary "object" files: The CPP file is the compiled file and is compiled without any knowledge about the other CPP files (or even libraries), unless fed to it through raw declaration or header inclusion. The CPP file is usually compiled into a .OBJ or a .O "object" file.

  2. The second is the linking together of all the "object" files, and thus, the creation of the final binary file (either a library or an executable).

Where does the HPP fit in all this process?

A poor lonesome CPP file...

The compilation of each CPP file is independent from all other CPP files, which means that if A.CPP needs a symbol defined in B.CPP, like:

// A.CPP
void doSomething()
{
   doSomethingElse(); // Defined in B.CPP
}

// B.CPP
void doSomethingElse()
{
   // Etc.
}

It won't compile because A.CPP has no way to know "doSomethingElse" exists... Unless there is a declaration in A.CPP, like:

// A.CPP
void doSomethingElse() ; // From B.CPP

void doSomething()
{
   doSomethingElse() ; // Defined in B.CPP
}

Then, if you have C.CPP which uses the same symbol, you then copy/paste the declaration...

COPY/PASTE ALERT!

Yes, there is a problem. Copy/pastes are dangerous, and difficult to maintain. Which means that it would be cool if we had some way to NOT copy/paste, and still declare the symbol... How can we do it? By the include of some text file, which is commonly suffixed by .h, .hxx, .h++ or, my preferred for C++ files, .hpp:

// B.HPP (here, we decided to declare every symbol defined in B.CPP)
void doSomethingElse() ;

// A.CPP
#include "B.HPP"

void doSomething()
{
   doSomethingElse() ; // Defined in B.CPP
}

// B.CPP
#include "B.HPP"

void doSomethingElse()
{
   // Etc.
}

// C.CPP
#include "B.HPP"

void doSomethingAgain()
{
   doSomethingElse() ; // Defined in B.CPP
}

How does include work?

Including a file will, in essence, parse and then copy-paste its content in the CPP file.

For example, in the following code, with the A.HPP header:

// A.HPP
void someFunction();
void someOtherFunction();

... the source B.CPP:

// B.CPP
#include "A.HPP"

void doSomething()
{
   // Etc.
}

... will become after inclusion:

// B.CPP
void someFunction();
void someOtherFunction();

void doSomething()
{
   // Etc.
}

One small thing - why include B.HPP in B.CPP?

In the current case, this is not needed, and B.HPP has the doSomethingElse function declaration, and B.CPP has the doSomethingElse function definition (which is, by itself a declaration). But in a more general case, where B.HPP is used for declarations (and inline code), there could be no corresponding definition (for example, enums, plain structs, etc.), so the include could be needed if B.CPP uses those declaration from B.HPP. All in all, it is "good taste" for a source to include by default its header.

Conclusion

The header file is thus necessary, because the C++ compiler is unable to search for symbol declarations alone, and thus, you must help it by including those declarations.

One last word: You should put header guards around the content of your HPP files, to be sure multiple inclusions won't break anything, but all in all, I believe the main reason for existence of HPP files is explained above.

#ifndef B_HPP_
#define B_HPP_

// The declarations in the B.hpp file

#endif // B_HPP_

or even simpler (although not standard)

#pragma once

// The declarations in the B.hpp file

It's the preprocessor way of declaring interfaces. You put the interface (method declarations) into the header file, and the implementation into the cpp. Applications using your library only need to know the interface, which they can access through #include.


C++ compilation

A compilation in C++ is done in 2 major phases:

  1. The first is the compilation of "source" text files into binary "object" files: The CPP file is the compiled file and is compiled without any knowledge about the other CPP files (or even libraries), unless fed to it through raw declaration or header inclusion. The CPP file is usually compiled into a .OBJ or a .O "object" file.

  2. The second is the linking together of all the "object" files, and thus, the creation of the final binary file (either a library or an executable).

Where does the HPP fit in all this process?

A poor lonesome CPP file...

The compilation of each CPP file is independent from all other CPP files, which means that if A.CPP needs a symbol defined in B.CPP, like:

// A.CPP
void doSomething()
{
   doSomethingElse(); // Defined in B.CPP
}

// B.CPP
void doSomethingElse()
{
   // Etc.
}

It won't compile because A.CPP has no way to know "doSomethingElse" exists... Unless there is a declaration in A.CPP, like:

// A.CPP
void doSomethingElse() ; // From B.CPP

void doSomething()
{
   doSomethingElse() ; // Defined in B.CPP
}

Then, if you have C.CPP which uses the same symbol, you then copy/paste the declaration...

COPY/PASTE ALERT!

Yes, there is a problem. Copy/pastes are dangerous, and difficult to maintain. Which means that it would be cool if we had some way to NOT copy/paste, and still declare the symbol... How can we do it? By the include of some text file, which is commonly suffixed by .h, .hxx, .h++ or, my preferred for C++ files, .hpp:

// B.HPP (here, we decided to declare every symbol defined in B.CPP)
void doSomethingElse() ;

// A.CPP
#include "B.HPP"

void doSomething()
{
   doSomethingElse() ; // Defined in B.CPP
}

// B.CPP
#include "B.HPP"

void doSomethingElse()
{
   // Etc.
}

// C.CPP
#include "B.HPP"

void doSomethingAgain()
{
   doSomethingElse() ; // Defined in B.CPP
}

How does include work?

Including a file will, in essence, parse and then copy-paste its content in the CPP file.

For example, in the following code, with the A.HPP header:

// A.HPP
void someFunction();
void someOtherFunction();

... the source B.CPP:

// B.CPP
#include "A.HPP"

void doSomething()
{
   // Etc.
}

... will become after inclusion:

// B.CPP
void someFunction();
void someOtherFunction();

void doSomething()
{
   // Etc.
}

One small thing - why include B.HPP in B.CPP?

In the current case, this is not needed, and B.HPP has the doSomethingElse function declaration, and B.CPP has the doSomethingElse function definition (which is, by itself a declaration). But in a more general case, where B.HPP is used for declarations (and inline code), there could be no corresponding definition (for example, enums, plain structs, etc.), so the include could be needed if B.CPP uses those declaration from B.HPP. All in all, it is "good taste" for a source to include by default its header.

Conclusion

The header file is thus necessary, because the C++ compiler is unable to search for symbol declarations alone, and thus, you must help it by including those declarations.

One last word: You should put header guards around the content of your HPP files, to be sure multiple inclusions won't break anything, but all in all, I believe the main reason for existence of HPP files is explained above.

#ifndef B_HPP_
#define B_HPP_

// The declarations in the B.hpp file

#endif // B_HPP_

or even simpler (although not standard)

#pragma once

// The declarations in the B.hpp file

Often you will want to have a definition of an interface without having to ship the entire code. For example, if you have a shared library, you would ship a header file with it which defines all the functions and symbols used in the shared library. Without header files, you would need to ship the source.

Within a single project, header files are used, IMHO, for at least two purposes:

  • Clarity, that is, by keeping the interfaces separate from the implementation, it is easier to read the code
  • Compile time. By using only the interface where possible, instead of the full implementation, the compile time can be reduced because the compiler can simply make a reference to the interface instead of having to parse the actual code (which, idealy, would only need to be done a single time).

It's the preprocessor way of declaring interfaces. You put the interface (method declarations) into the header file, and the implementation into the cpp. Applications using your library only need to know the interface, which they can access through #include.


Because in C++, the final executable code does not carry any symbol information, it's more or less pure machine code.

Thus, you need a way to describe the interface of a piece of code, that is separate from the code itself. This description is in the header file.


Because the people who designed the library format didn't want to "waste" space for rarely used information like C preprocessor macros and function declarations.

Since you need that info to tell your compiler "this function is available later when the linker is doing its job", they had to come up with a second file where this shared information could be stored.

Most languages after C/C++ store this information in the output (Java bytecode, for example) or they don't use a precompiled format at all, get always distributed in source form and compile on the fly (Python, Perl).


Responding to MadKeithV's answer,

This reduces dependencies so that code that uses the header doesn't necessarily need to know all the details of the implementation and any other classes/headers needed only for that. This will reduce compilation times, and also the amount of recompilation needed when something in the implementation changes.

Another reason is that a header gives a unique id to each class.

So if we have something like

class A {..};
class B : public A {...};

class C {
    include A.cpp;
    include B.cpp;
    .....
};

We will have errors, when we try to build the project, since A is part of B, with headers we would avoid this kind of headache...


C++ compilation

A compilation in C++ is done in 2 major phases:

  1. The first is the compilation of "source" text files into binary "object" files: The CPP file is the compiled file and is compiled without any knowledge about the other CPP files (or even libraries), unless fed to it through raw declaration or header inclusion. The CPP file is usually compiled into a .OBJ or a .O "object" file.

  2. The second is the linking together of all the "object" files, and thus, the creation of the final binary file (either a library or an executable).

Where does the HPP fit in all this process?

A poor lonesome CPP file...

The compilation of each CPP file is independent from all other CPP files, which means that if A.CPP needs a symbol defined in B.CPP, like:

// A.CPP
void doSomething()
{
   doSomethingElse(); // Defined in B.CPP
}

// B.CPP
void doSomethingElse()
{
   // Etc.
}

It won't compile because A.CPP has no way to know "doSomethingElse" exists... Unless there is a declaration in A.CPP, like:

// A.CPP
void doSomethingElse() ; // From B.CPP

void doSomething()
{
   doSomethingElse() ; // Defined in B.CPP
}

Then, if you have C.CPP which uses the same symbol, you then copy/paste the declaration...

COPY/PASTE ALERT!

Yes, there is a problem. Copy/pastes are dangerous, and difficult to maintain. Which means that it would be cool if we had some way to NOT copy/paste, and still declare the symbol... How can we do it? By the include of some text file, which is commonly suffixed by .h, .hxx, .h++ or, my preferred for C++ files, .hpp:

// B.HPP (here, we decided to declare every symbol defined in B.CPP)
void doSomethingElse() ;

// A.CPP
#include "B.HPP"

void doSomething()
{
   doSomethingElse() ; // Defined in B.CPP
}

// B.CPP
#include "B.HPP"

void doSomethingElse()
{
   // Etc.
}

// C.CPP
#include "B.HPP"

void doSomethingAgain()
{
   doSomethingElse() ; // Defined in B.CPP
}

How does include work?

Including a file will, in essence, parse and then copy-paste its content in the CPP file.

For example, in the following code, with the A.HPP header:

// A.HPP
void someFunction();
void someOtherFunction();

... the source B.CPP:

// B.CPP
#include "A.HPP"

void doSomething()
{
   // Etc.
}

... will become after inclusion:

// B.CPP
void someFunction();
void someOtherFunction();

void doSomething()
{
   // Etc.
}

One small thing - why include B.HPP in B.CPP?

In the current case, this is not needed, and B.HPP has the doSomethingElse function declaration, and B.CPP has the doSomethingElse function definition (which is, by itself a declaration). But in a more general case, where B.HPP is used for declarations (and inline code), there could be no corresponding definition (for example, enums, plain structs, etc.), so the include could be needed if B.CPP uses those declaration from B.HPP. All in all, it is "good taste" for a source to include by default its header.

Conclusion

The header file is thus necessary, because the C++ compiler is unable to search for symbol declarations alone, and thus, you must help it by including those declarations.

One last word: You should put header guards around the content of your HPP files, to be sure multiple inclusions won't break anything, but all in all, I believe the main reason for existence of HPP files is explained above.

#ifndef B_HPP_
#define B_HPP_

// The declarations in the B.hpp file

#endif // B_HPP_

or even simpler (although not standard)

#pragma once

// The declarations in the B.hpp file

Because C++ inherited them from C. Unfortunately.


Often you will want to have a definition of an interface without having to ship the entire code. For example, if you have a shared library, you would ship a header file with it which defines all the functions and symbols used in the shared library. Without header files, you would need to ship the source.

Within a single project, header files are used, IMHO, for at least two purposes:

  • Clarity, that is, by keeping the interfaces separate from the implementation, it is easier to read the code
  • Compile time. By using only the interface where possible, instead of the full implementation, the compile time can be reduced because the compiler can simply make a reference to the interface instead of having to parse the actual code (which, idealy, would only need to be done a single time).

Because C, where the concept originated, is 30 years old, and back then, it was the only viable way to link together code from multiple files.

Today, it's an awful hack which totally destroys compilation time in C++, causes countless needless dependencies (because class definitions in a header file expose too much information about the implementation), and so on.


Because the people who designed the library format didn't want to "waste" space for rarely used information like C preprocessor macros and function declarations.

Since you need that info to tell your compiler "this function is available later when the linker is doing its job", they had to come up with a second file where this shared information could be stored.

Most languages after C/C++ store this information in the output (Java bytecode, for example) or they don't use a precompiled format at all, get always distributed in source form and compile on the fly (Python, Perl).


Because C, where the concept originated, is 30 years old, and back then, it was the only viable way to link together code from multiple files.

Today, it's an awful hack which totally destroys compilation time in C++, causes countless needless dependencies (because class definitions in a header file expose too much information about the implementation), and so on.