|
Типы одного объекта или функции должны быть согласованы во всех их
описаниях. Должен быть согласован по типам и входной текст,
обрабатываемый транслятором, и связываемые части программы. Есть
простой, хотя и несовершенный, способ добиться согласованности
описаний в различных файлах. Это: включить во входные файлы,
содержащие операторы и определения данных, заголовочные файлы,
которые содержат интерфейсную информацию.
Средством включения текстов служит макрокоманда #include,
которая позволяет собрать в один файл (единицу трансляции)
несколько исходных файлов программы. Команда
#include "включаемый-файл"
заменяет строку, в которой она была задана, на содержимое файла включаемый-файл. Естественно, это содержимое должно быть текстом на С++, поскольку его будет читать транслятор. Как правило, операция включения реализуется отдельной программой, называемой препроцессором С++. Она вызывается системой программирования перед собственно трансляцией для обработки таких команд во входном тексте. Возможно и другое решение: часть транслятора, непосредственно работающая с входным текстом, обрабатывает команды включения файлов по мере их появления в тексте. В той системе программирования, в которой работает автор, чтобы увидеть результат команд включения файлов, нужно задать команду:
CC -E file.c
Эта команда для обработки файла file.c запускает препроцессор
(и только!), подобно тому, как команда CC без флага -E запускает сам
транслятор.
Для включения файлов из стандартных каталогов (обычно каталоги
с именем INCLUDE) надо вместо кавычек использовать угловые скобки
< и >. Например:
#include <stream.h> // включение из стандартного каталога #include "myheader.h" // включение из текущего каталога
Включение из стандартных каталогов имеет то преимущество, что имена этих каталогов никак не связаны с конкретной программой (обычно вначале включаемые файлы ищутся в каталоге /usr/include/CC, а затем в /usr/include). К сожалению, в этой команде пробелы существенны:
#include < stream.h> // <stream.h> не будет найден
Было бы нелепо, если бы каждый раз перед включением файла
требовалась его перетрансляция. Обычно включаемые файлы содержат
только описания, а не операторы и определения, требующие существенной
трансляторной обработки. Кроме того, система программирования
может предварительно оттранслировать заголовочные
файлы, если, конечно, она настолько развита, что способна сделать
это, не изменяя семантики программы.
Укажем, что может содержать заголовочный файл:
Определения типов struct point { int x, y; }; Шаблоны типов template<class T> class V { /* ... */ } Описания функций extern int strlen(const char*); Определения inline char get() { return *p++; } функций-подстановок Описания данных extern int a; Определения констант const float pi = 3.141593; Перечисления enum bool { false, true }; Описания имен class Matrix; Команды включения файлов #include <signal.h> Макроопределения #define Case break;case Комментарии /* проверка на конец файла */
Перечисление того, что стоит помещать в заголовочный файл, не является требованием языка, это просто совет по разумному использованию включения файлов. С другой стороны, в заголовочном файле никогда не должно быть:
Определений обычных функций char get() { return *p++; } Определений данных int a; Определений составных const tb[i] = { /* ... */ }; констант
По традиции заголовочные файлы имеют расширение .h, а файлы,
содержащие определения функций или данных, расширение .c. Иногда
их называют "h-файлы" или "с-файлы" соответственно. Используют
и другие расширения для этих файлов: .C, cxx, .cpp и
.cc. Принятое расширение вы найдете в своем справочном руководстве.
Макросредства описываются в $$4.7. Отметим только, что в С++ они
используются не столь широко, как в С, поскольку С++ имеет определенные
возможности в самом языке: определения констант (const),
функций-подстановок (inline), дающие возможность более простой
операции вызова, и шаблонов типа, позволяющие порождать семейство
типов и функций ($$8).
Совет помещать в заголовочный файл определения только простых,
но не составных, констант объясняется вполне прагматической причиной.
Просто большинство трансляторов не настолько разумно, чтобы
предотвратить создание ненужных копий составной константы. Вообще
говоря, более простой вариант всегда является более общим, а значит
транслятор должен учитывать его в первую очередь, чтобы создать
хорошую программу.