Введение

Конструирование в C и C++

В языках программирования C и C++ защита #include, иногда называемая защитой макроса, защитой заголовочного файла или защитой файла, представляет собой определенную конструкцию, используемую для предотвращения проблемы двойного включения при работе с директивой #include. Препроцессор C обрабатывает директивы вида #include <file> в исходном файле, находя соответствующий файл на диске и транслируя ("включая") его содержимое в копию исходного файла, известную как единица трансляции, заменяя при этом директиву #include. Файлы, включаемые таким образом, обычно являются заголовочными файлами, которые, как правило, содержат объявления функций и классов или структур. Если директива #include для данного файла встречается несколько раз во время компиляции (например, потому что она присутствует в нескольких других заголовочных файлах), файл обрабатывается снова каждый раз. Однако, если определенные конструкции языка C или C++ определены дважды, полученная единица трансляции становится недействительной. Защиты #include предотвращают возникновение этой ошибочной ситуации, определяя макрос препроцессора при первом включении заголовочного файла и обнаруживая его наличие, чтобы пропускать содержимое файла при последующих включениях. Добавление защиты #include в заголовочный файл – один из способов сделать этот файл идемпотентным. Другой способ борьбы с двойным включением – использование директивы #pragma once, которая не является стандартной, но почти повсеместно поддерживается компиляторами C и C++.

Файл "parent.h"

#include "grandparent.h"

Файл "child.c"

#include "grandparent.h"
#include "parent.h"

Пример

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

Файл "parent.h"

#include "grandparent.h"

Файл "child.c"

#include "grandparent.h"
#include "parent.h"

Обсуждение

Различные соглашения об именовании для макроса-защиты от многократного включения могут использоваться разными программистами. Другие распространенные формы приведенного выше примера включают GRANDPARENT INCLUDED, CREATORSNAME YYYYMMDD HHMMSS (с соответствующей информацией о времени) и имена, сгенерированные из UUID. (Однако имена, начинающиеся с одного подчеркивания и заглавной буквы (в C и C++) или содержащие двойное подчеркивание (только в C++), такие как GRANDPARENT_H и GRANDPARENT__H, зарезервированы для реализации языка и не должны использоваться пользователями.) Разумеется, важно избегать дублирования одного и того же имени макроса защиты от многократного включения в разных файлах заголовков, так как включение первого файла предотвратит включение второго, что приведет к потере всех объявлений, встроенных определений или других директив #include во втором файле заголовка.

Трудности

Для корректной работы защиты #include, каждая защита должна проверять и условно устанавливать различный макрос препроцессора. Следовательно, проект, использующий защиту #include, должен разработать последовательную схему именования для своих защит и убедиться, что эта схема не конфликтует со схемами именования заголовков сторонних библиотек или с именами глобально видимых макросов. По этой причине большинство реализаций C и C++ предоставляют нестандартную директиву #pragma once. Эта директива, помещенная в начало файла заголовка, гарантирует, что файл будет включен только один раз. Язык Objective-C (являющийся расширением языка C) представил директиву #import, которая работает аналогично #include, но включает каждый файл только один раз, что устраняет необходимость в защите #include.