Pragma once c что это
Перейти к содержимому

Pragma once c что это

  • автор:

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 :

  1. Эффективность: #pragma once более эффективен по сравнению с традиционным способом предотвращения двойного включения с использованием макроса #ifndef .
  2. Удобство: Директива делает код более лаконичным и читаемым, не требуя явного определения макросов и условных директив.
  3. Безопасность: Использование #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 будет включаться в них независимо друг от друга.

Да, точно. Спасибо 🙂

Внимание! Это довольно старый топик, посты в него не попадут в новые, и их никто не увидит. Пишите пост, если хотите просто дополнить топик, а чтобы задать новый вопрос — начните новый.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *