#ifdef и #ifndef директивы (C/C++)
Директивы #ifdef и препроцессоры имеют тот же эффект, что #if и #ifndef директива, когда она используется с оператором defined .
Синтаксис
#ifdef identifier
#ifndef identifier
Эти директивы эквивалентны следующим:
#if defined identifier
#if !defined identifier
Замечания
Вы можете использовать #ifdef директивы и #ifndef директивы в любом месте #if . Оператор #ifdef identifier эквивалентен #if 1 определению identifier . Это эквивалентно #if 0 тому, когда identifier не определено или не определено директивой #undef . Эти директивы проверяют наличие или отсутствие только идентификаторов, определенных с директивой #define , а не идентификаторов, объявленных в исходном коде C или C++.
Эти директивы предназначены только для совместимости с предыдущими версиями языка. Выражение defined( identifier ) константы, используемое #if с директивой, предпочтительнее.
Директива #ifndef проверка для противоположности условия проверка. #ifdef Если идентификатор не определен или его определение было удалено с #undef , условие имеет значение true (nonzero). В противном случае условие не выполняется (false, значение равно 0).
Блок, относящийся только к системам Майкрософт
Идентификатор можно передать из командной строки с помощью /D параметра. Можно указать /D до 30 макросов.
Директива #ifdef полезна для проверка, существует ли определение, так как определение можно передать из командной строки. Например:
// ifdef_ifndef.CPP // compile with: /Dtest /c #ifndef test #define final #endif
Завершение блока, относящегося только к системам Майкрософт
#ifdef u #ifndef
Другой метод условной компиляции состоит в использовании директив #ifdef и #ifndef, что соответственно означает «если определено» и «если не определено». Стандартный вид #ifdef следующий:
#ifdef имя_макроса
последовательность операторов
#endif
Если имя макроса определено ранее в операторе #define, то последовательность операторов, стоящих между #ifdef и #endif, будет компилироваться. Стандартный вид #ifndef следующий:
#ifndef имя_макроса
последовательность операторов
#endif
Если имя макроса не определено ранее в операторе #define, то последовательность операторов, стоящих между #ifdef и #endif, будет компилироваться.
Kaк #ifdef, так и #ifndef могут использовать оператор #else, но нe #elif. Например:
#include
#define TED 10
int main(void)
#ifdef TED
printf(«Hi Ted\n»);
#else
printf(«Hi anyone\n»);
#endif
#ifndef RALPH
printf(«RALPH not defined\n»);
#endif
return 0;
>
выводит «Hi Ted» и «RALPH not defined». Если TED не определен, то выведется «Hi anyone», a за ним «RALPH not defined».
#ifdef и #ifndef можно вкладывать друг в друга так же, как и #if.
Директивы #ifndef и #endif
Рассмотрим пример программы из раздела Классы в С++. Программа использует простейший класс, объект которого печатает сообщение. Заметьте, что раньше класс был объявлен в одном файле с главной функцией, но такой способ утилизации классов не эффективен, почему, было рассмотрено в разделе Классы в С++. Так вот, теперь объявление класса находится в файле message.h , а реализация методов класса определена в файле message.cpp , а classes.cpp — файл с main() функцией.
// интерфейс класса, файл: message.h class CppStudio // имя класса < public: // спецификатор доступа void message(); // функция (метод класса) выводящая сообщение на экран >; // конец объявления класса CppStudio
В h-файл помещен только лишь интерфейс класса, далее h-файл будет модифицирован с целью предотвращения ошибки многократного включения заголовочных файлов.
// реализация класса CppStudio, файл: message.cpp #include «stdafx.h» // связываем интерфейс класса с его реализацией #include «message.h» // подключаем прототип оператора cout #include void CppStudio::message() // функция (метод класса) выводящая сообщение на экран
Реализация данного класса включает в себя всего один метод, который печатает сообщение.
// classes.cpp: определяет точку входа для консольного приложения. #include "stdafx.h" #include using namespace std; #include "message.h" int main(int argc, char* argv[]) < CppStudio objMessage; // объявление объекта objMessage.message(); // вызов функции класса message system("pause"); return 0; >
// classes.cpp: определяет точку входа для консольного приложения. #include using namespace std; #include "message.h" int main(int argc, char* argv[]) < CppStudio objMessage; // объявление объекта objMessage.message(); // вызов функции класса message return 0; >
Структура данной программы совсем простая, чего не скажешь о программах с большим объёмом кода. В таких программах, как правило, код расположен не в одном файле. И подключение заголовочных файлов выполняется не обязательно в главном файле. Поэтому существует вероятность многократного включения в программу заголовочного файла, что в свою очередь приводит к ошибке компиляции. В случае с нашей программой, заголовочный файл подключен в главном файле в строке 7 и программа нормально работает без всяких ошибок. А если же заголовочный файл message.h , по ошибке, был подключён не только в главном файле программы, но, и ,например, в файле stdafx.h . В таком случае при компиляции программы появится следующая ошибка:
//Ошибка 1 error C2011: CppStudio: переопределение типа "class"
То есть, компилятор сообщает о многократном включении заголовочного файла с классом CppStudio . В таком случае придется потратить своё время и убрать лишнее включение в программу заголовочного файла. Чтобы не возникало такого рода ошибок, в С++ существует специальная структура кода, которую ещё называют препроцессорная обёртка.
// препроцессорная обёртка, файл: message.h с интерфейсом класса #ifndef CPPSTUDIO_H // если имя CPPSTUDIO_H ещё не определено #define CPPSTUDIO_H // определить имя CPPSTUDIO_H // определить класс CPPSTUDIO_H class CppStudio // имя класса < public: // спецификатор доступа void message(); // функция (метод класса) выводящая сообщение на экран >; // конец объявления класса CppStudio #endif CPPSTUDIO_H // если имя CPPSTUDIO_H уже определено, повторно не определять
Данная препроцессорная обёртка предотвращает попытку многократного включения заголовочных файлов. Препроцессорные директивы обрабатываются до этапа компиляции, программой-препроцессором. Который, в свою очередь не допускает многократного определения одного и того же класса. Препроцессорная обёртка похожа на оператор условного выбора if . Директива #ifndef проверяет, определено ли имя CPPSTUDIO_H , если нет, то управление передаётся директиве #define и определяется интерфейс класса. Если же имя CPPSTUDIO_H уже определено, управление передаётся директиве #endif . Таким образом, исключается возможность многократного определения класса CPPSTUDIO_H .
Обратите внимание на то, как написано имя класса, используемое в сочетании с директивами препроцессорной обёртки — CPPSTUDIO_H . Берётся имя заголовочного файла, в котором объявлен класс, причём имя принято записывать буквами верхнего регистра, а вместо точки ставить символ нижнего подчёркивания. Конечно же, имя может быть любым другим, но оно должно быть одинаковым во всех трёх директивах. Итак, структура препроцессорной обёртки такова:
// структура препроцессорной обёртки #ifndef /*ИМЯ ЗАГОЛОВОЧНОГО ФАЙЛА_H*/ #define /*ИМЯ ЗАГОЛОВОЧНОГО ФАЙЛА_H*/ // определение класса #endif /*ИМЯ ЗАГОЛОВОЧНОГО ФАЙЛА_H*/
С использованием препроцессорной обёртки, попытки подключения одного и того же файла, ошибки переопределения не вызовут. Этот же приём применяется и для предотвращения многократного определения функций, только если функции вынесены в отдельный файл.
К сожалению, для данной темы пока нет подходящих задач. Если у вас есть таковые на примете, отправте их по адресу: admin@cppstudio.com. Мы их опубликуем!
Что такое ifndef в C++?
#ifndef (или If Not Defined) — это директива препроцессора в языке программирования C++, используемая для предотвращения многократного включения кода, который уже был включен ранее. Это особенно важно при работе с заголовочными файлами.
// myheader.h #ifndef MYHEADER_H #define MYHEADER_H // Содержимое заголовочного файла class MyClass < public: void myFunction(); >; #endif // MYHEADER_H
Преимущества использования #ifndef:
- Предотвращение двойного включения: Директива #ifndef в сочетании с #define и #endif обеспечивает эффективный способ предотвращения многократного включения кода.
- Защита от конфликтов имен: Использование уникальных макросов в #ifndef устраняет возможные конфликты имен при включении нескольких заголовочных файлов.
- Улучшение производительности: Минимизация дублирования кода при многократном включении способствует повышению производительности компиляции.