#ifndef/#define VS #pragma once [дубликат]

448
22 февраля 2017, 21:42

На данный вопрос уже ответили:

  • Защита от множественного подключения заголовочного файла 1 ответ

Подскажите пожалуйста, в чём разница между двумя директивами и какую лучше использовать в коде?

Файл MyClass.h

#include <iostream>
#include <string>
#ifndef MYCLASS_H
#define MYCLASS_H
//class description
#endif

Или

#pragma once
#include <iostream>
#include <string>
//class description
Answer 1

Директива #pragma once является зависимой от реализации компилятора, а потому может не поддерживаться отдельными компиляторами, и будет просто игнорироваться.

Директивы #ifndef и #define являются стандартными директивами, а потому их использование в данном контексте будет одинаково для всех компиляторов.

Answer 2
Переносимость

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

В enWiki есть таблица со ссылками на источники. В этом списке только PGI не поддерживает #pragma once.
Присутствующий в списке на ruWiki OracleSolarisStudio исправил это упущение в версии 12.5

Ускорение компиляции

Этот плюс весьма призрачен. Ускорения компиляции от его использования вы можете не достичь, компиляторы умеют(gcc точно) оптимизировать и то, и то.

Коллизии

Основной минус include guards - вы не можете гарантировать, что придумали уникальный идентификатор.

Если вы никогда не планируете подключать чужеродные библиотеки, можете придумать себе правило для include guards и жить спокойно.
В ином случае, они ломают всё то, для чего в c++ существуют namespaces. Простого MYCLASS_H явно недостаточно, ведь он может быть уже определён в другой подключаемой библиотеке, либо если вы и пишете библиотеку - в клиентском коде.
Вам придётся выдумывать действительно уникальные идентификаторы, навроде __VENDOR_PACKAGE_MYCLASS или __MYCLASS_%TIMESTAMP%, чтобы исключить вероятность коллизии.

Резюме

Разумеется, что именно использовать в вашем проекте - решать только вам. Однако, очевидно, что нет никаких причин не использовать более лаконичную версию, кроме фанатичной "Отсутствует в стандарте".

PS: Вы некорректно применяете include guards - они обязательно должны охватывать весь файл целиком, иначе компилятор никак не сможет оптимизировать их, чтобы не запускать препроцессор для этого файла повторно.

Answer 3

Фундаментальное отличие заключается в том, что #pragma once относится ко всему заголовочному файлу целиком. Изначальной идеей #pragma once было то, что в процессе обработки отдельной единицы трансляции компилятор (препроцессор) имеет право даже и не искать и не открывать второй раз заголовочные файлы, которые содержат #pragma once. Именно в этой форме когда-то и предлагалось стандартизовать #pragma once - как средство ускорения компиляции.

Include guards #ifdef/#endif, понятное дело, имеют право покрывать не весь заголовочный файл, т.е. эта пара в общем случае не относится ко всему файлу целиком. Это означает, что в общем случае компилятор будет вынужден найти и открыть заголовочный файл, чтобы включить участки, не попавшие внутрь #ifdef/#endif.

В то же время понятно, что компилятору не составляет никакого труда проанализировать содержимое заголовочного файла при первом прочтении и распознать явное идиоматическое использование #ifdef/#endif, прокрывающее весь файл целиком от начала до конца. В такой ситуации #pragma once не предоставляет никакой практической выгоды по сравнению с #ifdef/#endif. Именно по этой причине от стандартизации #pragma once в свое время отказались - как от фичи, не привносящей никакой дополнительной ценности (кроме, разве что, компактной записи и того, что, как заметили @vp_arth и @VladD, нет необходимости выдумывать уникальный идентификатор).

Соответственно, ответ очевиден - пользуйтесь стандартной функциональностью #ifdef/#endif и забудьте про нестандартный #pragma once. Может быть в каком-то случае вы заметите, что компилятор (препроцессор) не хочет оптимизировать обработку на основе анализа #ifdef/#endif и использование #pragma once действительно ускоряет компиляцию... Тогда, если для вас это критично - может быть стоит добавить в ваши файлы #pragma once.

Еще одно соображение против стандартизации #pragma once заключалось в том, что для обеспечения жесткой гарантии ее спецификации (т.е. строго единственного включения), необходимо иметь возможность надежно определять идентичность файла по указанному в директиве #include пути в существующих файловых системах. Это задача в общем случае исключительно трудноразрешимая. Поэтому будьте осторожны с наивными пионерскими заявлениями типа "все современные компиляторы поддерживают #pragma once". Ни о какой поддержке строгой спецификации ни в одном компиляторе речи не идет. И, разумеется, ни о каких гарантиях одинаковости поведения между компиляторами речи не идет тоже. По этой причине, если вы таки соберетесь использовать #pragma once в своем коде, используйте ее вместе с include guards, а не вместо их.

P.S. Включение других заголовочных файлов обычно "кладут" внутрь вашего #ifdef/#endif. Им нет никакой причины находиться снаружи. Манерой оставлять их снаружи вы, возможно, рискуете подавить вышеупомянутую оптимизацию. Если уж вы пользуетесь #ifdef/#endif, то в подавляющем большинстве случаев они должны покрывать весь файл от начала до конца.

READ ALSO
функция в классе массив

функция в классе массив

Доброго времени суток! У меня есть класс массив

327
что не так в makefile

что не так в makefile

здравствуйте, есть такой makefile:

379
Ввод строк символами в двумерный вектор

Ввод строк символами в двумерный вектор

Имеется vector<vector<char>> и файл с текстомЦель: ввод этого текста в двумерный вектор

339
Не работает cin

Не работает cin

Задача - создать класс Группа, содержащий массив объектов класса Студент, и добавить в него меню для работы с группойЯ решил реализовать меню...

285