#include "my_windows_header.h"
#else
#include "my_linux_header.h"
#endif
Теперь, если кто-нибудь уже определил WINDOWS
до того, как компилятор увидел этот код, произойдет следующее:
#include "my_windows_header.h"
В противном случае будет включен другой заголовочный файл.
#include "my_linux_header.h"
Директива #ifdef WINDOWS
не интересуется, что собой представляет макрос WINDOWS
; она просто проверяет, был ли он определен раньше.
В большинстве крупных систем (включая все версии операционных систем) существуют макросы, поэтому вы можете их проверить. Например, можете проверить, как компилируется ваша программа: как программа на языке C++ или программа на языке C.
#ifdef __cplusplus
// в языке C++
#else
/* в языке C */
#endif
Аналогичная конструкция, которую часто называют
/* my_windows_header.h: */
#ifndef MY_WINDOWS_HEADER
#define MY_WINDOWS_HEADER
/* информация о заголовочном файле */
#endif
Директива #ifndef
проверяет, не было ли нечто определено раньше; например, #ifndef
противоположна директиве #ifdef
. С логической точки зрения эти макросы, использующиеся для контроля исходного файла, сильно отличаются от макросов, использованных для модификации исходного кода. Просто они используют одинаковый базовый механизм для выполнения своих функций.
27.9. Пример: интрузивные контейнеры
Контейнеры из стандартной библиотеки языка С++, такие как vector
и map
, являются неинтрузивными; иначе говоря, они не требуют информации о типах данных, использованных как их элементы. Это позволяет обобщить их для практически всех типов (как встроенных, так и пользовательских), поскольку эти типы допускают операцию копирования. Существует и другая разновидность контейнеров —
Определим двухсвязный список с девятью операциями.
void init(struct List* lst); /* инициализирует lst пустым */
struct List* create; /* создает новый пустой список
в свободной памяти */
void clear(struct List* lst); /* удаляет все элементы списка lst */
void destroy(struct List* lst); /* удаляет все элементы списка lst,
а затем удаляет сам lst */
void push_back(struct List* lst, struct Link* p); /* добавляет
элемент p в конец списка lst */
void push_front(struct List*, struct Link* p); /* добавляет элемент p
в начало списка lst */
/* вставляет элемент q перед элементом p in lst: */
void insert(struct List* lst, struct Link* p, struct Link* q);
struct Link* erase(struct List* lst, struct Link* p); /* удаляет
элемент p из списка lst */
/* возвращает элемент, находящийся за n до или через n узлов
после узла p:*/
struct Link* advance(struct Link* p, int n);
Мы хотим определить эти операции так, чтобы их пользователям было достаточно использовать только указатели List*
и Link*
. Это значит, что реализации этих функций можно кардинально изменять, не влияя на работу их пользователей. Очевидно, что выбор имен был сделан под влиянием библиотеки STL. Структуры List
и Link
можно определить очевидным и тривиальным образом.
struct List {
struct Link* first;
struct Link* last;
};
struct Link { /* узел двухсвязного списка */
struct Link* pre;
struct Link* suc;
};
Приведем графическое представление контейнера List
: