Продолжая развитие нашего проекта со стеком, создадим третий модуль, в котором объект класса TStack будет передаваться в функцию как параметр. Назовем этот файл function.срр (листинг 13.3).
Листинг 13.3. Содержимое файла function.срр
// пока стек не пустой // выводим число с вершины // удаляем элемент из стека void function (TStack &t) { while (!t.empty()) { cout << "(double *)t.top() << endl: double *p = (double *)t.pop();
delete p; } }
Проблем пока нет, но они появляются при подключении файла к нашей главной программе. Выясняется, что мы должны учитывать порядок следования директив #i nclude, чтобы все работало. Правильный порядок такой:
#include<iostream> using namespace std: #include "TStack.cpp" #include "function.cpp"
Стандартный файл iostream должен быть указан перед файлом function.cpp, так как в функции f () выполняется вывод в стандартный поток. Ранее должен быть включен и файл со стеком, так как функция f () его получает в качестве параметра. Порядок следования файлов TStack.cpp и iostream в данном случае может быть произвольным, но только потому, что в нашем стеке никак не используется стандартный ввод-вывод.
Налицо зависимость файлов друг от друга во время компиляции — ситуация крайне неприятная: мало того, что каждый раз транслируется объединенный текст, так еще мы должны «вручную» отслеживать связи между различными модулями. Это достижимо только для очень небольших программ, состоящих не более чем из пары десятков модулей, а дальше начинаются проблемы. На ум приходит естественное решение — добавить в модуль function.cpp три первые строки из модуля main.cpp:
#include<iostream> using namespace std; #include "TStack.cpp"
Однако при трансляции тут же выясняется, что класс TStack определен дважды1. По стандарту в программе разрешается иметь несколько определений класса, но в разных единицах трансляции (см. п. п. 3.2/5 в [1]). А у нас получается двойное определение в одной единице — в модуле main.cpp. Действительно, после постановки всех наших файлов (про системные файлы пока умолчим) объединенный модуль main.cpp содержит следующие директивы #include:
• #include<iostream> — включает системный файл iostream;
• #include "TStack.cpp" в модуле main.cpp — включает класс TStack;
• #include "function.cpp" в модуле main.cpp — включает файл function.cpp;
• #include "TStack.cpp" в модуле function.cpp — включает класс TStack.
Таким образом, класс TStack оказывается определенным дважды. Справиться с этой проблемой нам опять поможет препроцессор: мы должны определить во включаемом файле «стража» включения. Для этого используется директива #def ine и директивы условной трансляции препроцессора. Обычно это делается так:
// myfile.h
#ifndef MYFILE // страж определен?
#define MYFILE // если нет - определяем // содержимое файла
#endif// конец #ifndef
Первая строка часто пишется немного по-другому:
#if !defined( MYFILE ) // страж определен?
Однако сути дела это не меняет. Когда препроцессор обрабатывает первую директиву, имя MYFILE (страж) не определено:
Обратите внимание, что с системным файлом ошибок не возникает!
#include "myfile.h"
Поэтому содержимое файла включается в обработку. Если эта директива встречается еще раз, то страж уже определен и содержимое файла пропускается. Именно так организованы стандартные файлы, и поэтому не было проблем со стандартным файлом iostream. Например, начало стандартного файла math.h в интегрированной среде Borland С++ Builder 6 выглядит так:
/* math.h
Definitions for the math floating point package. ,
*/ /*
* C/C++ Run Time Library - Version 11.0
* Copyright (c) 1987, 2002 by Borland Software Corporation
* All Rights Reserved. */
/* $Revision: 9.17.2.6 $ */
#ifndef MATH_H // проверка стража
#define MATH_H // определение стража
Как видите, определен страж с именем МАТН_Н. Практически не отличается от
этого файла соответствующий файл системы Visual C++.NET 2003 (пропущены некоторые строки, не относящиеся к делу):
/***
¦math.h - definitions and declarations for math library *
* Copyright (c) Microsoft Corporation. All rights reserved. *
¦Purpose:
* This file contains constant definitions and external subroutine
* declarations for the math subroutine library.
* [ANSI/System V] *
* [public] ¦
* * * * /
#ifndef _INC_MATH #define _INC_MATH
В этой системе определен страж с именем _INC_MATH.
По негласному соглашению имена, определяемые в директиве #def i пе, пишутся прописными буквами — это позволяет по одному виду имени определить, что оно «принадлежит» препроцессору и «работает» только на стадии компиляции.
Наш файл TStack.cpp должен быть таким, как показано в листинге 13.4.
Листинг 13.4. Файл TStack.cpp со стражем
#ifndef __STACK // страж определен?
#define _STACK // определение стража class TStack { // определение класса }:
#endif /* _STACK */ // конец ifndef
Теперь все работает правильно.
Опубликовал Kest
January 15 2014 17:08:36 ·
3 Комментариев ·
6517 Прочтений ·
• Не нашли ответ на свой вопрос? Тогда задайте вопрос в комментариях или на форуме! •
Комментарии
Максим January 15 2014 18:26:00
По сути стражи включения я использую довольно редко в своей работе, но есть моменты, когда их использование вполне оправданно.
По мне так это дополнительные возможности самого языка, которые делают его более динамичным и гибким, особенно, когда требуется грамотная компоновка модулей при компиляции большого проекта.
Николай January 17 2014 14:55:25
Никогда не думал, что подключение файлов и библиотек влияет на работу программы, я файлы всегда подключал в произвольном порядке и ошибок не возникало, видимо при подключении своих файлов, я бы столкнулся со сложностями. Просто необходимо разобраться с препроцессорной обработкой, очень часто где встречалась, а я еще толком не знаю что и как там работает и какие команды за что отвечают, но с вашим сайтом думаю это не проблема! Спасибо, пример помог разобраться!
Илья January 18 2014 11:23:19
Николай, этот сайт вообще кладезь полезной информации, по крайней мере для меня. Мне он нравится потому что: 1 - Большое количество литературы, статей, а самое главное можно спросить совет у людей которые в этом действительно разбираются и понимают. Так что тут можно учиться и искать информацию, литературы предостаточно было бы желание как говориться. Если что-то не понимаешь можно спросить, и тебе подскажут. Мне как новичку в этом деле очень помогает. 2 – Общение. На сайте есть форум там можно найти друзей, которые тоже интересуются программированием или просто обсудить интересные новости с другими людьми.
Добавить комментарий
Рейтинги
Рейтинг доступен только для пользователей.
Пожалуйста, залогиньтесь или зарегистрируйтесь для голосования.
Нет данных для оценки.
Гость
Вы не зарегистрированны? Нажмите здесь для регистрации.