once pragma
Указывает, что компилятор включает файл заголовка только один раз при компиляции файла исходного кода.
Синтаксис
Замечания
Использование может сократить время сборки #pragma once , так как компилятор не откроет и снова считывает файл после первого #include файла в модуле перевода. Она называется оптимизацией с несколькими включениями. Он имеет эффект, аналогичный идиому охранника , который использует определения макросов препроцессора для предотвращения нескольких включений содержимого файла. Это также помогает предотвратить нарушения одного правила определения: требование, что все шаблоны, типы, функции и объекты не имеют более одного определения в коде.
// header.h #pragma once // Code placed here is included only once per translation unit
Мы рекомендуем директиву #pragma once для нового кода, так как она не засоряет глобальное пространство имен символами препроцессора. Он требует меньше ввода, это менее отвлекает, и он не может вызвать столкновения символов. Столкновения символов являются ошибками, вызванными, когда разные файлы заголовков используют один и тот же символ препроцессора, что и значение охранника. Он не является частью стандарта C++, но он реализуется переносимо несколькими общими компиляторами.
Нет преимуществ использования идиомы #pragma once guard и в одном и том же файле. Компилятор распознает идиом include guard и реализует оптимизацию с несколькими включениями так же, как #pragma once и директива, если код без комментариев или директива препроцессора не выполняется до или после стандартной формы идиома:
// header.h // Demonstration of the #include guard idiom. // Note that the defined symbol can be arbitrary. #ifndef HEADER_H_ // equivalently, #if !defined HEADER_H_ #define HEADER_H_ // Code placed here is included only once per translation unit #endif // HEADER_H_
Рекомендуется включить идиом guard, если код должен быть переносим для компиляторов, которые не реализуют #pragma once директиву, для обеспечения согласованности с существующим кодом или когда оптимизация с несколькими включениями невозможна. Это может происходить в сложных проектах, когда псевдоним файловой системы или псевдонимы включают пути, чтобы компилятор не идентифицировать идентичные файлы по каноническому пути.
Будьте осторожны, чтобы не использовать #pragma once или включить idiom guard в файлы заголовков, предназначенные для включения несколько раз, которые используют символы препроцессора для управления их эффектами. Пример этого проекта см. в файле заголовка . Кроме того, будьте осторожны, чтобы управлять путями включения, чтобы избежать создания нескольких путей к включенным файлам, что может победить оптимизацию с несколькими включениями как для охранников, так и #pragma once .
Что такое pragma once в C++?
#pragma once — это директива препроцессора в языке программирования C++, предназначенная для предотвращения многократного включения одного и того же заголовочного файла. Она обеспечивает более эффективный и безопасный способ предотвращения двойного включения.
// myheader.h #pragma once // Содержимое заголовочного файла class MyClass < public: void myFunction(); >;
Преимущества использования #pragma once :
- Эффективность: #pragma once более эффективен по сравнению с традиционным способом предотвращения двойного включения с использованием макроса #ifndef .
- Удобство: Директива делает код более лаконичным и читаемым, не требуя явного определения макросов и условных директив.
- Безопасность: Использование #pragma once исключает возможность опечаток при задании имен макросов, что уменьшает вероятность ошибок.
прагма один раз — pragma once
В языках программирования C и C ++ #pragma once не является стандартная, но широко поддерживаемая директива препроцессора , предназначенная для включения текущего исходного файла только один раз в одну компиляцию. Таким образом, #pragma once служит той же цели, что и include guards, но с рядом преимуществ, в том числе: меньший объем кода, предотвращение конфликтов имен, а иногда и повышение скорости компиляции. С другой стороны, #pragma once не обязательно доступен во всех компиляторах, и его реализация сложна и не всегда может быть надежной.
- 1 Пример
- 2 Преимущества
- 3 Предостережения
- 4 Переносимость
- 5 Ссылки
- 6 Внешние ссылки
Пример
Файл «grandparent.h»
#pragma once struct foo ;
Файл «parent.h»
#include "grandparent.h"
Файл «child.c»
#include "grandparent.h" #include "parent.h"
В этом Например, включение grandparent.h в parent.h и child.c обычно вызывает ошибку компиляции, потому что struct с данным именем может быть определено только один раз в данной компиляции. Директива #pragma once помогает избежать этого, игнорируя последующие включения grandparent.h .
Преимущества
Использование #pragma once позволяет Препроцессор C для включения файла заголовка, когда он необходим, и игнорирования директивы #include в противном случае. Это приводит к изменению поведения самого препроцессора C и позволяет программистам выражать зависимости файлов простым способом, устраняя необходимость в ручном управлении.
Наиболее распространенной альтернативой #pragma once является использование #define для установки макроса #include guard, имя которого выбранный программистом как уникальный для этого файла. Например,
#ifndef GRANDPARENT_H #define GRANDPARENT_H. содержимое grandparent.h #endif / *! GRANDPARENT_H * /
Этот подход минимально гарантирует, что содержимое включаемого файла не будет отображаться более одного раза. Это более подробный вариант, требует большего ручного вмешательства и подвержен ошибкам программиста, поскольку у компилятора нет доступных механизмов для предотвращения случайного использования одного и того же имени макроса более чем в одном файле, что приведет к созданию только одного из файлов. включены. Такие ошибки вряд ли останутся незамеченными, но могут усложнить интерпретацию отчета об ошибках компилятора. Поскольку за обработку #pragma once отвечает сам препроцессор, программист не может делать ошибки, которые вызывают конфликты имен.
При отсутствии #include guard вокруг директив #include использование #pragma once улучшит скорость компиляции для некоторых компиляторов, поскольку это механизм более высокого уровня; сам компилятор может сравнивать имена файлов или inodes без необходимости вызывать препроцессор C для сканирования заголовка на предмет #ifndef и #endif . Тем не менее, поскольку защита включения появляется очень часто и накладные расходы на открытие файлов значительны, компиляторы обычно оптимизируют обработку защиты включения, делая их так же быстро, как #pragma once .
Предостережения
Определить один и тот же файл в файловой системе — нетривиальная задача. Символические ссылки и особенно жесткие ссылки могут привести к тому, что один и тот же файл будет найден под разными именами в разных каталогах. Компиляторы могут использовать эвристику, которая сравнивает размер файла, время модификации и содержимое. Кроме того, #pragma once может делать неправильные вещи, если один и тот же файл намеренно скопирован в несколько частей проекта, например при подготовке сборки. В то время как include guards по-прежнему будет защищать от двойных определений, #pragma once может или не может обрабатывать их как один и тот же файл в зависимости от компилятора. Эти трудности, вместе с трудностями, связанными с определением того, что составляет один и тот же файл при наличии жестких ссылок, сетевых файловых систем и т. Д., До сих пор препятствовали стандартизации #pragma once .
Использование # Макросы include guard позволяют зависимому коду распознавать небольшие различия в семантике или интерфейсах конкурирующих альтернатив и реагировать на них. Например,
#include TLS_API_MACRO / *, определенный в командной строке * /. #if defined TLS_A_H. использовать один известный API #elif defined TLS_B_H. использовать другой известный API #else #error "нераспознанный TLS API "#endif
В этом случае прямое определение того, какой API доступен, будет использовать тот факт, что включаемый файл анонсировал себя с помощью макроса #include guard.
Директива #include определена для представления намерения программиста фактически включить текст файла в место директивы. Это может происходить несколько раз в пределах одной единицы компиляции и полезно для многократной оценки содержимого, содержащего макрос, на предмет изменения определений макроса.
Использование #pragma once , как и использование макросов #include guard во включаемом файле, возлагает ответственность на его авторов за защиту от нежелательного множественного включение. Чрезмерное использование любого механизма со стороны программистов путем прямого, незащищенного использования директив #include без собственных #include guard приведет к сбою при использовании включаемого файла, который не имеет защитил себя любым механизмом.
Переносимость
| Компилятор | #pragma once |
|---|---|
| Clang | Поддерживается |
| Comeau C / C ++ | Поддерживается |
| Cray C и C ++ | Поддерживается (начиная с 9.0) |
| C ++ Builder XE3 | Поддерживается |
| Digital Mars C ++ | Поддерживается |
| GCC | Поддерживается (официально с 3.4) |
| HP C / aC ++ | Поддерживается (начиная с A.06.12) |
| IBM XL C / C ++ | Поддерживается (начиная с 13.1.1) |
| Компилятор Intel C ++ | Поддерживается |
| Microsoft Visual C ++ | Поддерживается ( с версии 4.2) |
| Компилятор NVIDIA CUDA | Поддерживается (в зависимости от базового компилятора хоста) |
| Поддерживается | |
| Поддерживается | |
| Поддерживается | |
| Поддерживается | |
| Oracle Developer Studio C / C ++ | Поддерживается (начиная с версии 12.5) |
| Portland Group C / C ++ | Поддерживается (начиная с версии 17.4) |
| TinyCC | Поддерживается (с апреля 2015 года) |
| TASKING VX-toolset для TriCore: компилятор C | Поддерживается (начиная с v6.2r2) |
Ссылки
Внешние ссылки
Для чего нужна команда pragma once?
Здравствуйте. Очень часто встречаю конструкцию #pragma once в чужих исходниках. Скажите, для чего она нужна?
Илья, директива препроцессора #pragma once применяется для защиты от «двойного подключения» заголовочных файлов.
Например, у нас есть 3 файла — main.cpp , header.h и something.cpp .
header.h
class Foo < public: void method(); private: int member; >;
something.cpp
#include "header.h" void Foo::method()
main.cpp
#include #include "header.h" int main() < Foo *obj = new Foo; obj->method(); return 0; >
Файл header.h подключается одновременно в main.cpp и something.cpp . Из за этого объявление класса Foo будет выполнено более одного раза, и мы получим ошибку при компиляции.
Для того, чтобы избежать подобных проблем, используется директива #pragma once , которая указывает препроцессору, что файл подключается только один раз.
#pragma once class Foo < public: void method(); private: int member; >;
Еще один вариант — include guard, который в отличии от #pragma once , умеет корректно работать с символическими ссылками.
Большое спасибо! Буду разбираться.
На счёт #pragma once — всё правильно. Более того, Микрософт пишет, что эта прагма сокращает время компиляции, поскольку файлы обрабатываются только один раз. Сильно подозреваю, что в этом случае для компиляции требуется больше оперативной памяти — бесплатный сыр, как известно.
А вот твой пример не совсем корректный.
В данном случае будет профит только от сокращения времени компиляции. Двойного определения класса Foo здесь не будет в любом случае.
Файлы main.cpp и something.cpp являются отдельными единицами компиляции и встретятся только на этапе сборки, поэтому header.h будет включаться в них независимо друг от друга.
Для двойного включения нужна немного другая конструкция:
header1.h
#include void bar(std::ostream & os, int arg);
main1.cpp
#include #include "header1.h" int main() < // . bar(std::cout, 10); >
Вот в этом случае заголовочный файл должен быть защищён от двойного включения, поскольку при обработке файла main1.cpp сначала он включается явно директивой #include , а затем неявно при включении заголовочного файла header1.h.
Причём файл something1.cpp, содержащий реализацию функции bar() , мы здесь не рассматриваем, поскольку это опять-таки отдельная единица компиляции.
Замечание для начинающих. При компиляции каждый файл .cpp сначала обрабатывается препроцессором. Препроцессор — штука по своей логике достаточно примитивная: читает текст программы из входного файла, обрабатывает директивы препроцессора ( #include , #define , #if/#elif/#else/#endif и пр., но не #pragma (!) — это не директива препроцессора) и макросы и пишет получившийся текст программы во временный файл, откуда его потом подхватывает компилятор. Найдя директиву #include , препроцессор тупо заменяет её на содержимое указанного в директиве файла и продолжает обрабатывать получившийся текст.
Файлы main.cpp и something.cpp являются отдельными единицами компиляции и встретятся только на этапе сборки, поэтому header.h будет включаться в них независимо друг от друга.
Да, точно. Спасибо 🙂
Внимание! Это довольно старый топик, посты в него не попадут в новые, и их никто не увидит. Пишите пост, если хотите просто дополнить топик, а чтобы задать новый вопрос — начните новый.