Опубликован: 26.06.2003 | Доступ: свободный | Студентов: 36634 / 5382 | Оценка: 4.07 / 3.80 | Длительность: 15:14:00
ISBN: 978-5-9556-0017-8
Лекция 14:

Компоновка программ, препроцессор

< Лекция 13 || Лекция 14: 123 || Лекция 15 >

Использование включаемых файлов

В языке Си++ реализовано удобное решение. Можно поместить объявления классов и функций в отдельный файл и включать этот файл в начало других файлов с помощью директивы #include.

#include "Book.h"
. . .
Book b;

Фактически директива   #include подставляет содержимое файла Book.h в текущий файл перед тем, как начать его компиляцию. Эта подстановка осуществляется во время первого прохода компилятора по программе – препроцессора. Файл Book.h называется файлом заголовков.

В такой же файл заголовков можно поместить прототипы функций и включать его в другие файлы, там, где функции используются.

Таким образом, текст программы на языке Си++ помещается в файлы двух типов – файлы заголовков и файлы программ. В большинстве случаев имеет смысл каждый класс помещать в отдельный файл, вернее, два файла – файл заголовков для объявления класса и файл программ для определения класса. Имя файла обычно состоит из имени класса. Для файла заголовков к нему добавляется окончание " .h " (иногда, особенно в системе Unix, " .hh " или " .H "). Имя файла программы – опять-таки имя класса с окончанием " .cpp " (иногда " .cc " или " .C ").

Объединять несколько классов в один файл стоит лишь в том случае, если они очень тесно связаны и один без другого не используется.

Включение файлов может быть вложенным, т.е. файл заголовков может сам использовать директиву #include. Файл Book.h выглядит следующим образом:

#ifndef __BOOK_H__
#define __BOOK_H__

// включить файл с объявлением используемого
// здесь базового класса
#include "Item.h"
#include "String.h"   
// объявление класса String

// объявление класса Book
class Book : public Item
{
public:
. . .
private:
          String title;
     . . . 
}; #endif

Обратите внимание на первые две и последнюю строки этого файла. Директива #ifndef начинает блок так называемой условной компиляции, который заканчивается директивой #endif. Блок условной компиляции – это кусок текста, который будет компилироваться, только если выполнено определенное условие. В данном случае условие заключается в том, что символ __BOOK_H__ не определен. Если этот символ определен, текст между #ifndef и #endif не будет включен в программу. Первой директивой в блоке условной компиляции стоит директива #define, который определяет символ __BOOK_H__ как пустую строку.

Давайте посмотрим, что произойдет, если в какой-либо .cpp -файл будет дважды включен файл Book.h:

#include "Book.h"
. . .
#include "Book.h"

Перед началом компиляции текст файла Book.h будет подставлен вместо директивы #include:

#ifndef __BOOK_H__
#define __BOOK_H__
. . .
class Book
{
. . .
};
#endif
. . .
#ifndef __BOOK_H__
#define __BOOK_H__
. . .
class Book
{
. . .
};
#endif

В самом начале символ __BOOK_H__ не определен, и блок условной компиляции обрабатывается. В нем определяется символ __BOOK_H__. Теперь условие для второго блока условной компиляции уже не выполняется, и он будет пропущен. Таким образом, объявление класса Book будет вставлено в файл только один раз. Разумеется, написание два раза подряд директивы #include с одинаковым аргументом легко поправить. Однако структура заголовков может быть очень сложной. Чтобы избежать необходимости отслеживать все вложенные заголовки и искать, почему какой-либо файл оказался вставленным дважды, можно применить изложенный выше прием и существенно упростить себе жизнь.

Еще одно замечание по составлению заголовков. Включайте в заголовок как можно меньше других заголовков. Например, в заголовок Book.h необходимо включить заголовки Item.h и String.h, поскольку класс Book использует их. Однако если используется лишь имя класса без упоминания его содержимого, можно обойтись и объявлением этого имени:

#include "Item.h"
#include "String.h"

class Annotation;   
// Annotation – имя некоего класса

class Book : public Item
{
public:
     Annotation* CreateAnnotation();
private:
     String title;
};

Объявление класса Item требуется знать целиком, для того, чтобы обработать объявление класса Book, т.е. компилятору надо знать все методы и атрибуты Item, чтобы включить их в класс Book. Объявление класса String также необходимо знать целиком, по крайней мере, для того, чтобы правильно вычислить размер экземпляра класса Book. Что же касается класса Annotation, то ни размер его объектов, ни его методы не важны для определения содержимого объекта класса Book. Единственное, что надо знать, это то, что Annotation есть имя некоего класса, который будет определен в другом месте.

Общее правило таково, что если объявление класса использует указатель или ссылку на другой класс и не задействует никаких методов или атрибутов этого класса, достаточно объявления имени класса. Разумеется, полное объявление класса Annotation понадобится в определении метода CreateAnnotation.

Компилятор поставляется с набором файлов заголовков, которые описывают все стандартные функции и классы. При включении стандартных файлов обычно используют немного другой синтаксис:

#include <string.h>
< Лекция 13 || Лекция 14: 123 || Лекция 15 >
Елена Шумова
Елена Шумова

Здравствуйте! Я у Вас прошла курс Язык программировая Си++.

Заказала сертификат. Хочу изменить способ оплаты. Как это сделать?

Маргарита Башкатова
Маргарита Башкатова
Анатолий Федоров
Анатолий Федоров
Россия, Москва, Московский государственный университет им. М. В. Ломоносова, 1989
Рустам Новиков
Рустам Новиков
Эстония, Таллин