2.1. Обеспечение единственности подключения заголовочного файла

2.1. Обеспечение единственности подключения заголовочного файла

Проблема

У вас есть заголовочный файл, который подключается несколькими другими файлами. Вы хотите убедиться, что препроцессор сканирует объявления в заголовочном файле не более одного раза.

Решение

В заголовочном файле с помощью #define определите макрос и содержимое заголовочного файла подключайте только тогда, когда макрос еще не был определен. Используйте такую комбинацию директив препроцессора #ifndef, #define и #endif, как я делаю в примере 2.1:

#ifndef MYCLASS_H__ // защита #include

#define MYCLASS_H__

// Здесь поместите все. что требуется...

#endif // MYCLASS_H__

Когда препроцессор сканирует такой заголовочный файл, одной из первых встреченных им вещей будет директива #ifndef и следующий за ней символ, #ifndef говорит препроцессору перейти на следующую строку только в том случае, если символ MYCLASS_H__ еще не определен. Если он уже определен, препроцессор должен пропустить код до закрывающей директивы #endif. Строка, следующая за #ifndef, определяет MYCLASS_H__, так что если этот файл при одной и той же компиляции сканируется препроцессором дважды, то второй раз MYCLASS_H__ будет уже определен. Поместив весь код между #ifndef и #endif, вы гарантируете, что в процессе компиляции он будет прочитан только один раз.

Обсуждение

Если вы не используете эту методику, которая называется защитой заголовка, то вы, вероятно, уже видели ошибки компиляции «symbol already defined» (символ уже определен), которые являются следствием отсутствия защитных мер против множественных определений. C++ не позволяет определять один и тот же символ несколько раз, и если вы это сделаете (целенаправленно или случайно), то получите ошибку компилятора. Включение защиты предотвращает такие ошибки, и она стала стандартной методикой.

Определяемый с помощью #define макрос не обязан следовать какому-либо формату, но использованный мной синтаксис имеет широкое распространение. Его идея состоит в том, чтобы использовать символ, который не будет конфликтовать с другим макросом, в результате чего файл будет непреднамеренно пропускаться препроцессором. На практике вы можете столкнуться и с другими методиками, такими как включение в макрос версии заголовочного файла или модуля, т.е. MYCLASS_H_V301__, или, возможно, имени автора. Не имеет значения, как вы его назвали, до тех пор, пока вы придерживаетесь единой схемы. Эти макросы должны использоваться только в заголовочном файле, который они защищают, и больше нигде.

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

#ifndef MYCLASS_H__

#include "myclass.h"

#endif

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

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