Порядок выполнения работы
Ниже приведен листингпростой многофайловой программы, в которой определены типы данных «Точка» (структура Pointв файле Point.h) и «Прямоугольник» (структура Rectв файле Rect.h). Поскольку второй тип данных определяется через первый, вфайле Rect.hимеетсядиректива#include«Point.h». В основном модуле main.cppпросто создается объект типа «Прямоугольник» и выводятся координаты его левого верхнего и правого нижнего углов. В основном модуле используются как функции из модуля Point.срр. так и функции из модуля Rect.cpp. поэтому в него включены оба заголовочных файла Point.hи Rect.h. Но после обработки этих директив препроцессором окажется, что структура Pointопределена дважды. В результате компилятор выдаст сообщение об ошибке наподобие следующего: «error...; 'Point'; 'struct* typeredefinition».
///////// Файл Point.h//////////
// Объявления типов
structPoint
{
ititx;
inty;
};
// Прототипыфункций
voidSetXY(Point& point, int x. int y>;
intGetX(Point& point);
intGetY(Point& point);
////////// ФайлRect.h ////////////
♦include “Point.h”
// Объявлениятипов
structRect
{
Point leftTop;
Point rightBottom;
};
// Прототипыфункций
voidSetLTRB(Rect&rect, Point lt, Point rb);
voidGetLT(Rect&rect, Point<);
voidGetRB(Rect&rect, Point&rb);
////////// Файл Point.cpp ///////////
#include “Poin.h”
voidSetXY(Poin& point, int x, int y)
{
point.x=x;
poin.y=y;
}
intGetX(Point& point)
{
returnpoint.x;
}
intGetY(Point& point)
{
return point.у;
}
/////////// Файл Rect.cpp ///////////
#include “Rect.h”
voidSetLTRB(Rect&rect, Point lt. Point rb)
{
rect. leftTop = lt;
rect.rightBottom = rb;
}
voidGetLT(Rect& rect. Point<)
{
lt = rect.leftTop;
}
voidGetRB(Rect&rect, Point&rb)
{
rb = rect.rightBottom;
}
#include <stdio.h>
#include “Point.h”
#include “Rect.h”
/////////////// ФайлMain.cpp //////////////
int main()
{
Point ptl, pt2, lt. rb;
Rectrectl;
SetXV(ptl, 2, 5);
SetXY(pt2, 10, 14);
SetLTRB(rect1, ptl, pt2);
GetLT(rect1, lt);
GetRB(rect1, rb);
printf(“rect.lt.x = %d, rect.lt.y = %d\n, lt.x, lt.y);
printf(“rect.rb.x = %d, rect.rb.y = %d\n”, rb.x, rb.y);
return0;
}
Каков выход из этой ситуации? Бьерн Страуструп рекомендует использовать так называемые стражи включения, и этот способ нашел широкое применение. Он состоит в следующем: чтобы предотвратить повторное включение заголовочных файлов, содержимое каждого h-файла должно находиться между директивамиусловной компиляции #ifndefи #endif, как описано ниже:
#ifndef FILENAME_H
#define FILENAME_H
/* содержимое заголовочного файла */
#endif/* FILFNAME_H*/
Применительно к нашему примеру файл Point.hдолжеч содержать следующий текст:
//////////// ФайлPoint.h ///////////////
#ifndef P0INT_H
#define POINT_H
// Объявлениятипов
struct Point
{
int x;
inty;
};
//Прототипыфункций
voidSetXY(Point& point,int x, int y);
intGetX(Point& point);
intGetV(Point&point);
#endif/*P0INT_H*/