header guards c что такое

2.11 – Защита заголовков

Проблема повторяющегося определения

В уроке «2.6 – Предварительные объявления и определения» мы отметили, что идентификатор переменной или функции может иметь только одно определение (правило одного определения). Таким образом, программа, которая определяет идентификатор переменной более одного раза, вызовет ошибку компиляции:

Точно так же программы, которые определяют функцию более одного раза, также вызовут ошибку компиляции:

Хотя эти программы легко исправить (удалить повторяющееся определение), с помощью заголовочных файлов довольно легко попасть в ситуацию, когда определение в заголовочный файл включается более одного раза. Это может произойти, если заголовочный файл включает с #include другой заголовочный файл (что является обычным явлением).

Рассмотрим следующий академический пример:

Таким образом, после разрешения всех директив #include файл main.cpp будет выглядеть так:

Защита заголовка

Хорошей новостью является то, что мы можем избежать указанной выше проблемы с помощью механизма, называемого защитой заголовка (или защитой включения). Защита заголовка – это директивы условной компиляции, которые имеют следующую форму:

Все ваши заголовочные файлы должны иметь защиту заголовков. Имя SOME_UNIQUE_NAME_HERE может быть любым, но по соглашению устанавливается равным полному имени заголовочного файла, набранному заглавными буквами, с использованием подчеркивания вместо пробелов и знаков препинания. Например, у square.h будет защита заголовка будет следующей:

Даже заголовочные файлы стандартной библиотеки используют защиту заголовков. Если бы вы взглянули на заголовочный файл iostream из Visual Studio, вы бы увидели:

Для продвинутых читателей

Обновление нашего предыдущего примера с помощью защиты заголовков

После того, как препроцессор разрешит все включения, эта программа будет выглядеть так:

Как видно из примера, второе включение содержимого square.h (из geometry.h ) игнорируется потому, что SQUARE_H уже был определен при первом включении. Следовательно, функция getSquareSides включается только один раз.

Защита заголовков не препятствует одиночным включениям заголовка в разные файлы исходного кода.

Обратите внимание, что цель защиты заголовков – предотвратить получение файлом исходного кода более одной копии защищенного заголовка. По замыслу, защита заголовков не препятствует включению данного заголовочного файла (однократно) в отдельные исходные файлы. Это также может вызвать непредвиденные проблемы. Рассмотрим следующую возможность:

Разве мы не можем просто избежать определений в файлах заголовков?

Обычно мы советуем вам не включать определения функций в заголовки. Итак, вам может быть интересно, зачем вам включать защиту заголовков, если она защищают вас от чего-то, чего вы не должны делать.

В будущем мы покажем вам довольно много случаев, когда в файл заголовка необходимо поместить определения, не являющиеся функциями. Например, C++ позволяет вам создавать свои собственные типы. Эти пользовательские типы обычно определяются в файлах заголовков, чтобы эти определения можно было распространить на исходные файлы, которые должны их использовать. Без защиты заголовков ваши исходные файлы могут иметь несколько идентичных копий этих определений, что приведет к ошибке компиляции повторяющихся определений.

Таким образом, хотя на данном этапе этой серии обучающих статей не обязательно иметь защиту заголовков, но мы вырабатываем хорошие привычки, чтобы вам не приходилось отказываться от вредных привычек позже.

#pragma once

Многие компиляторы поддерживают более простую альтернативную форму защиты заголовков с помощью директивы #pragma :

#pragma когда-то служила той же цели, что и защита заголовков, а ее дополнительное преимущество заключается в том, что она короче и менее подвержена ошибкам.

Однако #pragma once не является официальной частью языка C++, и не все компиляторы поддерживают ее (хотя большинство современных компиляторов поддерживает).

В целях совместимости мы рекомендуем придерживаться традиционной защиты заголовков. Она не требуют много работы и гарантированно поддерживаются всеми компиляторами.

Резюме

Защита заголовков предназначена для того, чтобы содержимое заданного заголовочного файла не копировалось более одного раза в любой отдельный файл, чтобы предотвратить дублирование определений.

Обратите внимание, что дублирование объявлений – это нормально, поскольку объявление может быть объявлено несколько раз без инцидентов, но даже если ваш заголовочный файл состоит только из объявлений (без определений), всё равно рекомендуется включать защиту заголовков.

Читайте также:  away mode в биосе что это такое

Обратите внимание, что защита заголовков не предотвращает копирование содержимого заголовочного файла (один раз) в отдельные файлы проекта. Это хорошо потому, что нам часто нужно ссылаться на содержимое заданного заголовка из разных файлов проекта.

Небольшой тест

Вопрос 1

Добавьте защиту заголовка в этот заголовочный файл:

Источник

2.11 — Header guards

The duplicate definition problem

Similarly, programs that define a function more than once will also cause a compile error:

While these programs are easy to fix (remove the duplicate definition), with header files, it’s quite easy to end up in a situation where a definition in a header file gets included more than once. This can happen when a header file #includes another header file (which is common).

Consider the following academic example:

This seemingly innocent looking program won’t compile! Here’s what’s happening. First, main.cpp #includes square.h, which copies the definition for function getSquareSides into main.cpp. Then main.cpp #includes geometry.h, which #includes square.h itself. This copies contents of square.h (including the definition for function getSquareSides) into geometry.h, which then gets copied into main.cpp.

Thus, after resolving all of the #includes, main.cpp ends up looking like this:

Duplicate definitions and a compile error. Each file, individually, is fine. However, because main.cpp ends up #including the content of square.h twice, we’ve run into problems. If geometry.h needs getSquareSides(), and main.cpp needs both geometry.h and square.h, how would you resolve this issue?

The good news is that we can avoid the above problem via a mechanism called a header guard (also called an include guard). Header guards are conditional compilation directives that take the following form:

When this header is #included, the preprocessor checks whether SOME_UNIQUE_NAME_HERE has been previously defined. If this is the first time we’re including the header, SOME_UNIQUE_NAME_HERE will not have been defined. Consequently, it #defines SOME_UNIQUE_NAME_HERE and includes the contents of the file. If the header is included again into the same file, SOME_UNIQUE_NAME_HERE will already have been defined from the first time the contents of the header were included, and the contents of the header will be ignored (thanks to the #ifndef).

All of your header files should have header guards on them. SOME_UNIQUE_NAME_HERE can be any name you want, but by convention is set to the full filename of the header file, typed in all caps, using underscores for spaces or punctuation. For example, square.h would have the header guard:

Even the standard library headers use header guards. If you were to take a look at the iostream header file from Visual Studio, you would see:

For advanced readers

In large programs, it’s possible to have two separate header files (included from different directories) that end up having the same filename (e.g. directoryA\config.h and directoryB\config.h). If only the filename is used for the include guard (e.g. CONFIG_H), these two files may end up using the same guard name. If that happens, any file that includes (directly or indirectly) both config.h files will not receive the contents of the include file to be included second. This will probably cause a compilation error.

Because of this possibility for guard name conflicts, many developers recommend using a more complex/unique name in your header guards. Some good suggestions are a naming convention of

Updating our previous example with header guards

Let’s return to the square.h example, using the square.h with header guards. For good form, we’ll also add header guards to geometry.h.

After the preprocessor resolves all of the includes, this program looks like this:

Читайте также:  райхон трава на каком языке

As you can see from the example, the second inclusion of the contents of square.h (from geometry.h) gets ignored because SQUARE_H was already defined from the first inclusion. Therefore, function getSquareSides only gets included once.

Header guards do not prevent a header from being included once into different code files

Note that the goal of header guards is to prevent a code file from receiving more than one copy of a guarded header. By design, header guards do not prevent a given header file from being included (once) into separate code files. This can also cause unexpected problems. Consider:

Note that square.h is included from both main.cpp and square.cpp. This means the contents of square.h will be included once into square.cpp and once into main.cpp.

Let’s examine why this happens in more detail. When square.h is included from square.cpp, SQUARE_H is defined until the end of square.cpp. This define prevents square.h from being included into square.cpp a second time (which is the point of header guards). However, once square.cpp is finished, SQUARE_H is no longer considered defined. This means that when the preprocessor runs on main.cpp, SQUARE_H is not initially defined in main.cpp.

The end result is that both square.cpp and main.cpp get a copy of the definition of getSquareSides. This program will compile, but the linker will complain about your program having multiple definitions for identifier getSquareSides!

Now when the program is compiled, function getSquareSides will have just one definition (via square.cpp), so the linker is happy. File main.cpp is able to call this function (even though it lives in square.cpp) because it includes square.h, which has a forward declaration for the function (the linker will connect the call to getSquareSides from main.cpp to the definition of getSquareSides in square.cpp).

Can’t we just avoid definitions in header files?

We’ve generally told you not to include function definitions in your headers. So you may be wondering why you should include header guards if they protect you from something you shouldn’t do.

There are quite a few cases we’ll show you in the future where it’s necessary to put non-function definitions in a header file. For example, C++ will let you create your own types. These user-defined types are typically defined in header files, so the type definitions can be propagated out to the code files that need to use them. Without a header guard, a code file could end up with multiple (identical) copies of a given type definition, which the compiler will flag as an error.

So even though it’s not strictly necessary to have header guards at this point in the tutorial series, we’re establishing good habits now, so you don’t have to unlearn bad habits later.

Many compilers support a simpler, alternate form of header guards using the #pragma directive:

#pragma once serves the same purpose as header guards, and has the added benefit of being shorter and less error-prone.

However, #pragma once is not an official part of the C++ language, and not all compilers support it (although most modern compilers do).

For compatibility purposes, we recommend sticking to traditional header guards. They aren’t much more work and they’re guaranteed to be supported on all compliant compilers.

Header guards are designed to ensure that the contents of a given header file are not copied more than once into any single file, in order to prevent duplicate definitions.

Note that header guards do not prevent the contents of a header file from being copied (once) into separate project files. This is a good thing, because we often need to reference the contents of a given header from different project files.

Читайте также:  delphi mt22u чем шить

Источник

Урок №23. Header guards и #pragma once

Обновл. 11 Сен 2021 |

На этом уроке мы рассмотрим, что такое header guards и #pragma once в языке C++, а также зачем они нужны и как их правильно использовать.

Проблема дублирования объявлений

Как мы уже знаем из урока о предварительных объявлениях, идентификатор может иметь только одно объявление. Таким образом, программа с двумя объявлениями одной переменной получит ошибку компиляции:

То же самое касается и функций:

Рассмотрим следующую программу:

Эта, казалось бы, невинная программа, не скомпилируется! Проблема кроется в определении функции в файле math.h. Давайте детально рассмотрим, что здесь происходит:

Сначала main.cpp подключает заголовочный файл math.h, вследствие чего определение функции getSquareSides копируется в main.cpp.

Затем main.cpp подключает заголовочный файл geometry.h, который, в свою очередь, подключает math.h.

В geometry.h находится копия функции getSquareSides() (из файла math.h), которая уже во второй раз копируется в main.cpp.

Таким образом, после выполнения всех директив #include, main.cpp будет выглядеть следующим образом:

Мы получим дублирование определений и ошибку компиляции. Если рассматривать каждый файл по отдельности, то ошибок нет. Однако в main.cpp, который подключает сразу два заголовочных файла с одним и тем же определением функции, мы столкнемся с проблемами. Если для geometry.h нужна функция getSquareSides(), а для main.cpp нужен как geometry.h, так и math.h, то какое же решение?

Header guards

На самом деле решение простое — использовать header guards (защиту подключения в языке C++). Header guards — это директивы условной компиляции, которые состоят из следующего:

Источник

Header guards in C++ and C

At LearnCpp.com | 1.10 — A first look at the preprocessor. Under Header guards, there are those code snippets:

In implementing the header guard, it is mentioned as follows:

5 Answers 5

The FILENAME_H is a convention. If you really wanted, you could use #ifndef FLUFFY_KITTENS as a header guard (provided it was not defined anywhere else), but that would be a tricky bug if you defined it somewhere else, say as the number of kittens for something or other.

The result of preprocessing one implementation («.cpp») file is a translation unit (TU).

Headers can include other headers, so a header may be indirectly included multiple times within the same TU. (Your mymath.h is an example of this.)

Definitions can only occur at most once per TU. (Some definitions must also not be in multiple TUs; this case is slightly different and not discussed here.)

The problem include guards solve is preventing multiple definition errors when a given header is included more than once within one TU.

Include guards work by «wrapping» the contents of the header in such a way that the second and subsequent includes are no-ops. The #ifndef/#define directives should be the first two lines of the file, and #endif should be the last.

Include guards are only used in headers. Do not define your main function in a header: put it in an implementation file.

If you have a header that will define a type and declare a function, but also needs a header itself:

«Wrapping» it with include guards gives the complete contents of the file:

The name used for the include guard must be unique, otherwise conflicting names will give confusing results. These names are only simple macros, and there is nothing in the language which enforces a certain style. However, project conventions usually impose requirements. There are several different include guard naming styles you can find here on SO and elsewhere; this answer gives good criteria and a good overview.

Источник

Сказочный портал