Include guards
Back to the office. One memo sometimes refers to another and asks for a copy of config.h to be attached to it. If a department asks for both memos, the clerk puts config.h into the folder twice—once directly, and once along with the memo that refers to it.
The same thing happens with headers. The header config.h defines the constant constexpr int kMaxUsers = 100;. The header users.h includes config.h, and main.cpp includes both—and does not compile:
#include "config.h"
#include "users.h" // ✗
int main() {
return kMaxUsers;
}error: redefinition of 'kMaxUsers'Where the second definition comes from
The preprocessor inserts text and checks nothing in it. The text of config.h reaches main.cpp twice: directly and through users.h. The translation unit ends up with two definitions of kMaxUsers, and the compiler rejects them. A function declaration can repeat any number of times, but a definition cannot.
Paste one #include at a time and watch how many times the text of config.h reaches main.cpp, then turn a guard on.
constexpr int kMaxUsers = 100;#include "config.h" int count_users();
#include "config.h"#include "users.h" int main() { return kMaxUsers;}
#include "config.h" int report() { return kMaxUsers;}
#include "config.h"waiting#include "users.h"waiting- int main() {
- return kMaxUsers;
- }
#include "config.h"waiting- int report() {
- return kMaxUsers;
- }
The preprocessor walks the #include lines from top to bottom and puts each file's text in place of the line. Press the button.
Two forms of a guard
An include guard keeps a header's text from entering a translation unit twice. In the office, it is a mark on the folder: "config.h is already inside." A guard comes in two forms.
#pragma once
The shortest guard is a single line at the top of the header:
#pragma once
constexpr int kMaxUsers = 100;When the preprocessor meets an #include of this file in the same translation unit a second time, it skips the file. #pragma once is not part of the C++ standard, but GCC, Clang and MSVC all support it.
#ifndef, #define and #endif
The standard form is three directives:
#ifndef CONFIG_Hat the top of the file reads as "if the macroCONFIG_His not defined";#define CONFIG_Hright after it;#endifat the end of the file.
#ifndef CONFIG_H
#define CONFIG_H
constexpr int kMaxUsers = 100;
#endifThe macro CONFIG_H is created with no text: it serves only as a mark that says "the header is already inserted." The preprocessor goes through this file like this:
- On the first include,
CONFIG_His not defined yet:#ifndeflets the text through, and#definedefines the macro. - On the second include,
CONFIG_His already defined, and#ifndefskips everything up to#endif.
The macro's name comes from the file's name, in capitals, so that two headers never share it.
One translation unit
The guard works within one translation unit. Every other .cpp file that includes the header gets its own copy of its text. The mark sits on one folder, and the other folders do not have it.
Common mistake
The header users.h was made as a copy of config.h, and the macro's name was never changed:
#ifndef CONFIG_H // ✗
#define CONFIG_H
int count_users();
#endifThe file main.cpp includes config.h first, then users.h, and calls count_users(). By the time the preprocessor reaches users.h, the macro CONFIG_H is already defined, and the whole text of users.h disappears. The compiler finds no declaration:
error: use of undeclared identifier 'count_users'Give every header a macro of its own: USERS_H for users.h.
Key points
#includeinserts text word for word, so a header can enter a translation unit twice.- The compiler rejects a second definition within one translation unit.
#pragma onceor the trio#ifndef,#define,#endifskips the repeated insertion.- A guard works within one translation unit, not across the whole program.
Subscribe to take the course
The course is free. Sign in with Telegram and subscribe to the channel to open every lesson.