Зачем вам нужна защита включения для заголовочных файлов C ++? - PullRequest
0 голосов
/ 01 ноября 2018

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

Ответы [ 4 ]

0 голосов
/ 01 ноября 2018

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

Что касается варианта использования, где включение одного и того же заголовочного файла более одного раза может быть полезным; один случай, где я нашел это полезным, был для моделирования класса шаблонного контейнера в C (потому что C не поддерживает шаблоны непосредственно). У меня был файл-контейнера-реализации-заголовка, который выглядел примерно так (но более детально; здесь я показываю упрощенную версию для удобства чтения):

// MyContainerImplemention.h
// be sure to #define MYTYPE and MYARRAYSIZE
// before #include-ing this file!

struct ArrayOf##MYTYPE
{
   MYTYPE arrayOfItems[MYARRAYSIZE];
};

inline void Set##MYTYPE##Item(struct ArrayOf##MyType * container, int which, MYTYPE item) 
{
   container[which] = item;
}

[... and so on for various other MYTYPE-specific methods ...]

... тогда мои .c файлы могут делать что-то вроде:

#define MYTYPE int
#define MYARRAYSIZE 10
#include "MyContainerImplementation.h"
#undef MYARRAYSIZE
#undef MYTYPE

#define MYTYPE short
#define MYARRAYSIZE 15
#include "MyContainerImplementation.h"
#undef MYARRAYSIZE
#undef MYTYPE

struct ArrayOfint myInts;
struct ArrayOfshort myShorts;

SetintItem(&myInts, 5, 12);
SetshortItem(&myShorts, 3, 2);
[...]

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

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

0 голосов
/ 01 ноября 2018

Без включения охранников или #pragma после того, как компилятору придется вести список включенных файлов. Это нелегко из-за различных возможных путей к этим файлам (а однажды #pragma не может полностью решить эту проблему) и будет ожидать немного большей части оригинальных компиляторов C, которые должны были работать с очень ограниченной памятью.

0 голосов
/ 01 ноября 2018

Защитные блоки или #pragma once включены для предотвращения многократного включения файла.

#pragma once, хотя поддерживается большинством компиляторов, не является официальной частью стандарта c ++ и может работать не на всех компиляторах. Вы можете использовать защитный блок, который будет работать на любом компиляторе. Примером защитного блока в файле MyClass.hpp будет:

#ifndef MYCLASS_HPP
#define MYCLASS_HPP

//Code here

#endif
0 голосов
/ 01 ноября 2018

То, что сегодня верно, не обязательно верно, когда появился C и был создан препроцессор C, на котором основан C ++.

#pragma once - это просто шаг к созданию надлежащих модулей C ++ , так что это досадное историческое наследие окончательно устранено.

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

...