C++ Include Conflicts and #pragma once

An introduction to a big problem with the include directive, and two different methods ways to work around it.
This lesson is part of the course:

Intro to C++ Programming

Become a software engineer with C++. Starting from the basics, we guide you step by step along the way

3D art showing a mechanic character
Ryan McCombe
Ryan McCombe
Posted

In the previous lesson, we saw how we could split our code into multiple files, and then use the #include directive to bring it back together before compilation.

We can think of the #include directive as simply copying and pasting code from one file into another. That's quite crude, and it's not long before we encounter a problem. For example, lets imagine we have a file containing our Character class.

class Character {};

Lets create a Monster class in another file.

#include "Character.cpp"

class Monster : public Character {};

This new file is referencing the Character class, so we #include the Character.cpp file so the compiler knows what that is.

So far, so good. Unfortunately, if we now try to #include our files into a third file (for example, our main.cpp) our program will fail to compile.

#include "Character.cpp"
#include "Monster.cpp"

int main() {
  Character PlayerCharacter;
  Monster Goblin;
}

This will generate an error similar to below:

error: redefinition of ‚Äėclass Character‚Äô

C++ error: redefinition of class

To understand the error, lets imagine what our main.cpp might look like as the preprocessor executes our #include directives. First, it includes Character.cpp and Monster.cpp:

1// From #include "Character.cpp":
2class Character {};
3
4// From #include "Monster.cpp":
5#include "Character.cpp"
6class Monster : public Character {};
7
8int main() {
9  Character PlayerCharacter;
10  Monster Goblin;
11}
12

Note in particular the effect of #include Monster.cpp - highlighted on line 5. Because Monster.cpp also included Character.cpp, we will now end up with the contents of Character.cpp being placed into this file twice:

// From "Character.cpp":
class Character {};

// From "Monster.cpp":
class Character {};
class Monster : public Character {};

int main() {
  Character PlayerCharacter;
  Monster Goblin;
}

Hopefully now, the issue is clearer - our preprocessor directives are resulting in our main.cpp file defining the Character class twice, hence the compilation error message we see.

Avoiding Indirect Dependencies

When following the above steps, you might have two possible ideas how to fix it. We could simply delete some #include directives. After all, if main.cpp can get the Character class through including Monster.cpp, it doesn't need to include Character.cpp directly.

That is true, but it is generally not a good idea to rely on this. If one of our files has a dependency, it is much better to be explicit and clear about that, rather than relying on some side effects or implementation quirks that are going on in other files.

Otherwise, if someone opens our file in the future, it might be quite difficult to understand where Character comes from, or indeed why our code works at all.

Worse, if someone makes a seemingly innocous change like reordering include directives, it might be quite difficult for them to understand why the code is no longer compiling.

Therefore, we should strive to be explicit in our code - particularly around dependencies. We should always directly express what our code needs to be in place for it to work.

So, lets see some better ways to prevent the preprocessor from duplicating our includes.

C++ Header Guard using #ifndef and #define

We can protect ourselves from this situation by using the conditional inclusion techniques we saw in the previous lesson.

This pattern is commonly called a header guard, because it is most commonly used with header files. Header files will be the topic of the next lesson.

By combining the #ifndef (if not defined) directive with a #define directive, we can insure that a block of code is only included once in any file. Typically, when used as a header guard, the entire contents of the file will be between the ifndef and endif directives:

#ifndef CHARACTER
#define CHARACTER

class Character {};

#endif

To understand how this solves the problem, lets walk through main.cpp as seen by the preprocessor again, except this time, lets do it with our header guard for character.cpp in place.

After evaluating all of our include directives, the preprocessor will then be seeing this:

1// From #include "Character.cpp":
2#ifndef CHARACTER
3#define CHARACTER
4class Character {};
5#endif
6
7// From #include "Monster.cpp":
8#ifndef CHARACTER
9#define CHARACTER
10class Character {};
11#endif
12class Monster : public Character {};
13
14int main() {
15  Character PlayerCharacter;
16  Monster Goblin;
17}
18

Next, it's going to evaluate our conditional inclusion directives.

Line 2 is checking if a macro called CHARACTER is not defined in our file. This is true - CHARACTER is not defined, so lines 2 and 3 get included:

1// From #include "Character.cpp":
2#define CHARACTER
3class Character {};
4
5// From #include "Monster.cpp":
6#ifndef CHARACTER
7#define CHARACTER
8class Character {};
9#endif
10class Monster : public Character {};
11
12int main() {
13  Character PlayerCharacter;
14  Monster Goblin;
15}
16

Line 2 now defines a CHARACTER macro for the rest of this file. So, when we reach line 6, CHARACTER is defined this time. Therefore, lines 6-9 get excluded.

This leaves us with a totally valid main.cpp file:

// From #include "Character.cpp":
class Character {};

// From #include "Monster.cpp":
class Monster : public Character {};

int main() {
  Character PlayerCharacter;
  Monster Goblin;
}

Our choice to call our macro CHARACTER in the previous example has no connection to the fact that our class is called Character. The preprocessor is not aware of C++ syntax.

It is conventional to define a meaningful name, but any unique name would have worked for us.

Test your Knowledge

What problem is a header guard intended to solve?

How can we implement a header guard using conditional inclusion to prevent a file containing a Weapon class from being included twice?

C++ #pragma once Header Guard

The combined use of the #ifndef #define and #endif for this use case is so common that most compilers have made a single preprocessor directive that can replace the pattern.

This is the #pragma once directive. If we had a file like the following example:

#ifndef CHARACTER
#define CHARACTER
class Character
{
  // class definition here
}
#endif

We could instead simplify it to this:

#pragma once
class Character
{
  // class definition here
}

#pragma once is not part of the C++ standard, but it is included by every modern build tool.

We can use either the full conditional inclusion approach, or the #pragma once shortcut. Different developers (and companies) will have different preferences on what to use.

"Do not use #pragma once [...] All header files should have #define guards to prevent multiple inclusion." - Google Style Guide

"All headers should protect against multiple includes with the #pragma once directive. Note that all compilers we use support #pragma once." - Unreal Engine Style Guide

We will use the #pragma once approach in all our examples going forward.

Test your Knowledge

Aside from the #ifndef technique, what is another way to implement a header guard?

Always use Header Guards

Our example above only applied header guards to the file that was specifically causing the problem - Character.cpp

But, we don't know how our code files are going to be included in the future, so it is recommended to include header guards proactively.

Even if Monster.cpp is not being included multiple times right now, it may be in the future. It's generally going to be much easier to protect against these issues ahead of time than trying to fix them as they crop up.

#pragma once
#include "Character.cpp"

class Monster : public Character {};

Up next, we'll introduce the concept of Header Files which will show us a more industry-standard way to organise our projects.

Was this lesson useful?

Ryan McCombe
Ryan McCombe
Posted
This lesson is part of the course:

Intro to C++ Programming

Become a software engineer with C++. Starting from the basics, we guide you step by step along the way

The Preprocessor and the Linker
3D art showing a progammer setting up a development environment
This lesson is part of the course:

Intro to C++ Programming

Become a software engineer with C++. Starting from the basics, we guide you step by step along the way

Free, unlimited access!

This course includes:

  • 66 Lessons
  • Over 200 Quiz Questions
  • Capstone Project
  • Regularly Updated
  • Help and FAQ
Next Lesson

C++ Header Files

Learn what header files are, why we might want to use them, and get practice implementing them.
3D art showing a character in a bar
Contact|Privacy Policy|Terms of Use
Copyright © 2023 - All Rights Reserved