Включение заголовочных файлов в C/С++ более одного раза - программирование
Подтвердить что ты не робот

Включение заголовочных файлов в C/С++ более одного раза

Полезно ли включать заголовочный файл более одного раза в C или С++?

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

Edit:

Я понимаю, что существуют стандартные способы делать такие вещи, как включить охранников и pragma once, но зачем вам нужно указывать даже это? Не должно ли поведение компилятора по умолчанию включать файлы только один раз?

4b9b3361

Ответ 1

Да, это полезно при создании кода с препроцессором или при выполнении трюков, таких как Boost.PP.

В качестве примера см. X Макросы. Основная идея заключается в том, что файл содержит тело макроса, а вы #define аргументы, а затем #include. Здесь надуманный пример:

macro.xpp

std::cout << MESSAGE;
#undef MESSAGE

file.cpp:

int main() {
# define MESSAGE "hello world"
# include "macro.xpp"
}

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

Ответ 2

Да, включая заголовок более одного раза, может быть полезно (хотя это довольно необычно). Канонический пример <assert.h>, который определяет asssert по-разному в зависимости от того, определена ли NDEBUG или нет. Таким образом, имеет смысл включить его, затем иметь (обычно условное) определение NDEBUG, а затем включать его снова, с (по крайней мере потенциально) различными определениями assert:

Макрос assert переопределяется в соответствии с текущим состоянием NDEBUG каждый раз, когда <assert.h> включен 1.

Большинство заголовков, однако, приходят к некоторым болям, чтобы быть идемпотентными (т.е. иметь одинаковые эффекты независимо от того, как часто они включаются).


1 C99, §7.2/1.

Ответ 3

Типичный пример (непроверенный) - точка, заключающаяся в том, что он составляет список перечислений, поэтому они последовательно отображаются в enum и в потоковом коде:

// states.h
X(Uninitialised)
X(Initialised)
X(Running)
X(Complete)
X(Shutdown)

// app.c++
#if defined(X)
# error X is already defined
#endif

enum States {
    #define X(TAG) TAG,
    #include "states.h"
    #undef X
    Extra_So_Trailing_Comma_Ignored
};

std::ostream& operator<<(std::ostream& os, const States& state)
{
    #define X(TAG) if (state == TAG) return os << #TAG;
    #include "states.h"
    #undef X
    return os << "<Invalid-State>";
}

Ответ 4

Да, было бы удобнее только один раз включить его, и именно поэтому вы используете #pragma один раз. в С++:)

Edit:

Примечание: #pragma один раз не переносится. Вы можете использовать

#ifndef FILENAME_H
#define FILENAME_H

в верхней части ваших файлов заголовков, если вы хотите, чтобы он был переносимым.

Ответ 5

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

Классическим примером может быть перечисление C/С++ и соответствующие строки, которые более или менее выглядят следующим образом:

// MYENUM_VALUES.H
MYENUMVALUE(a)
MYENUMVALUE(b)
MYENUMVALUE(c)
MYENUMVALUE(d)

// MYENUM.H
#define MYENUMVALUE(x) x,
enum MyEnum
{
#include "MYENUM_VALUES.H"
}
#undef MYENUMVALUE

// MYENUMTOSTRING.C
#define MYENUMVALUE(x) case x : return #x;

const char * MyEnumToString(MyEnum val)
{
    switch (val)
    {
    #include "MYENUM_VALUES.H"
    default return "unknown";
    }
} 
#undef MYENUMVALUE

Ответ 6

#ifndef _INC_HEADER_
#define _INC_HEADER_

//header code

#endif

Где HEADER - имя заголовка

например. main_win.h будет _INC_MAIN_WIN_