Почему правильно включать заголовочный файл дважды в c ++? - PullRequest
5 голосов
/ 21 августа 2010
#include "DLLDefines.h"
#include "DLLDefines.h"

Вышесказанное фактически прошло компиляцию, но почему?

Ответы [ 7 ]

13 голосов
/ 21 августа 2010

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

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

Например, все стандартные заголовки библиотеки (например, string или vector), вероятно, включены в большинство ваших заголовков.Таким образом, вы быстро получаете один и тот же заголовок, который косвенно включается несколько раз в один и тот же файл .cpp.

Короче говоря, у него есть , иначе весь код C ++ развалится.

Что касается , как это работает, обычно через охранников.Помните, что #include просто выполняет простое копирование / вставку: он вставляет содержимое файла заголовка на сайт #include.

Итак, допустим, у вас есть файл заголовка header.h со следующим содержимым:

class MyClass {};

теперь давайте создадим файл cpp, который включает его дважды:

#include "header.h"
#include "header.h"

препроцессор расширяет это до:

class MyClass {};
class MyClass {};

, что, очевидно, вызывает ошибку:один и тот же класс определяется дважды.Так что это не работает.Вместо этого давайте изменим заголовок так, чтобы он содержал include guard :

#ifndef HEADER_H
#define HEADER_H

class MyClass {};

#endif

Теперь, если мы включим его дважды, мы получим это:

#ifndef HEADER_H
#define HEADER_H

class MyClass {};

#endif

#ifndef HEADER_H
#define HEADER_H

class MyClass {};

#endif

И эточто происходит, когда препроцессор обрабатывает его:

#ifndef HEADER_H // HEADER_H is not defined, so we enter the "if" block
#define HEADER_H // HEADER_H is now defined

class MyClass {};// MyClass is now defined

#endif           // leaving the "if" block

#ifndef HEADER_H // HEADER_H *is* defined, so we do *not* enter the "if" block
//#define HEADER_H
//
//class MyClass {};
//
#endif           // end of the skipped "if" block

Итак, конечный результат состоит в том, что MyClass был определен только один раз , даже если заголовок был включен дважды.И поэтому полученный код действителен.

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

8 голосов
/ 21 августа 2010

зависит от заголовочного файла; языковые ограничения для нескольких включений одного и того же файла отсутствуют.

Некоторые файлы предназначены для включения несколько раз (например, <assert.h> может быть включено несколько раз для включения и выключения assert).

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

6 голосов
/ 21 августа 2010

include не имеет ничего общего с языком C или C ++.Это указание препроцессору ввести файл.Препроцессор не заботится о том, какой файл вводится, и не должен.Это может быть вполне приемлемо для этого:

void Foo::SomeFunc(int cond)
{
    switch (cond) {
    case kThisCase:
#include "longFirstCase.h"
        break;
    case kSecondCase:
#include "longSecondCase.h"
        break;
    case kThirdCase:
#include "longFirstCase.h"
#include "longSecondCase.h"
        break;
    }
}

Я видел один и тот же файл, включенный несколько раз в качестве механизма конфигурации.

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

4 голосов
/ 21 августа 2010

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

#ifndef DLLDEFINES_H
#define DLLDEFINES_H
// your code
#endif
2 голосов
/ 21 августа 2010

Пока многократное включение заголовочных файлов не нарушает ODR (одно правило определения) $ 3.2, код является корректным.

1 голос
/ 21 августа 2010

Он называется , включая охрану .

#ifndef GRANDFATHER_H
#define GRANDFATHER_H

struct foo {
    int member;
};

#endif

Цитата из Википедии:

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

См. Ссылку выше для получения дополнительной информации.

0 голосов
/ 21 августа 2010

DLLDefines.h может также иметь #pragma один раз наверху, #pragma один раз гарантирует, что файл будет включен только один раз.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...