Главная · Поиск книг · Поступления книг · Top 40 · Форумы · Ссылки · Читатели

Настройка текста
Перенос строк


    Прохождения игр    
Demon's Souls |#15| Dragon God
Demon's Souls |#14| Flamelurker
Demon's Souls |#13| Storm King
Demon's Souls |#12| Old Monk & Old Hero

Другие игры...


liveinternet.ru: показано число просмотров за 24 часа, посетителей за 24 часа и за сегодня
Rambler's Top100
Образование - Страустрап Б. Весь текст 579.17 Kb

Язык С++

Предыдущая страница Следующая страница
1 ... 13 14 15 16 17 18 19  20 21 22 23 24 25 26 ... 50
согласованность  типов,   которые  используются   в  разных  файлах
программы.  Функции  обсуждаются  довольно  подробно.  Сюда  входят
передача  параметров,   параметры  по  умолчанию,  перегрузка  имен
функций, и,  конечно же,  описание и  определение функций.  В конце
описываются макросы.

     4.1 Введение

  Иметь всю  программу в  одном файле  обычно невозможно, поскольку
коды стандартных  библиотек и операционной системы находятся где-то
в другом  месте. Кроме  того, хранить  весь текст  пользовательской
программы в  одном файле как правило непрактично и неудобно. Способ
организации программы  в файлы  может помочь читающему охватить всю
структуру программы,  а также  может дать  возможность  компилятору
реализовать эту  структуру. Поскольку  единицей компиляции является
файл, то во всех случаях, когда в файл вносится изменение (сколь бы
мало оно  ни было),  весь файл нужно компилировать заново. Даже для
программы    умеренных    размеров    время,    затрачиваемое    на
перекомпиляцию,  можно  значительно  снизить  с  помощью  разбиения
программы на файлы подходящих размеров.
  Рассмотрим пример  с калькулятором.  Он был  представлен  в  виде
одного исходного файла. Если вы его набили, то у вас наверняка были
небольшие трудности  с расположением описаний в правильном порядке,
и пришлось  использовать по меньшей мере одно "фальшивое" описание,
чтобы  компилятор   смог  обработать  взаимно  рекурсивные  функции
expr(), term()  и prim().  В тексте  уже отмечалось,  что программа
состоит из  четырех  частей  (лексического  анализатора,  программы
синтаксического разбора,  таблицы имен и драйвера), но это никак не
было отражено  в тексте  самой программы. По сути дела, калькулятор
был написан  по-другому. Так  это не  делается; даже  если  в  этой
программе "на  выброс" пренебречь  всеми соображениями  методологии
программирования, эксплуатации  и эффективности  компиляции,  автор
все равно  разобьет эту  программу в 200 строк на несколько файлов,
чтобы програмировать было приятнее.
  Программа,  состояшая   из  нескольких   раздельно  компилируемых
файлов, должна  быть согласованной  в смысле  использования имен  и
типов, точно так же, как и программа, состоящая из одного исходного
файла. В принципе, это может обеспечить и компоновщик*. Компоновщик
- это  программа, стыкующая отдельно скомпилированные части вместе.
____________________
  * или линкер. (прим. перев.)

                             - стр 110 -

Компоновщик  часто   (путая)   называют   загрузчиком.   В   UNIX'е
компоновщик  называется   ld.  Однако   компоновщики,  имеющиеся  в
большинстве систем,  обеспечивают очень  слабую поддержку  проверки
согласованности.
  Программист может скомпенсировать недостаток поддержки со стороны
компоновщика,  предоставив   дополнительную  информацию   о   типах
(описания). После  этого согласованность  программы  обеспечивается
проверкой согласованности  описаний, которые  находятся в  отдельно
компилируемых частях.  Средства, которые  это обеспечивают, в вашей
системе будут. C++ разработан так, чтобы способствовать такой явной
компоновке**.

     4.2 Компоновка

  Если не указано иное, то имя, не являющееся локальным для функции
или класса,   в  каждой части  программы,  компилируемой  отдельно,
должно относиться  к одному  и тому  же типу, значению, функции или
объекту. То  есть, в  программе может  быть только один нелокальный
тип, значение,  функция  или  объект  с  этим  именем.  Рассмотрим,
например, два файла:

  // file1.c:
      int a = 1;
      int f() { /* что-то делает */ }

  // file2.c:
      extern int a;
      int f();
      void g() { a = f(); }

a и f(), используемые g() в файле file2.c,- те же, что определены в
файле file1.c.  Ключевое  слово  extern  (внешнее)  указывает,  что
описание  a   в  file2.c   является  (только)   описанием,   а   не
определением. Если  бы a  инициализировалось, extern было бы просто
проигнорировано,  поскольку   описание  с   инициализацией   всегда
является  определением.  Объект  в  программе  должен  определяться
только один  раз. Описываться  он может  много раз,  но типы должны
точно согласовываться. Например:

  // file1.c:
      int a = 1;
      int b = 1;
      extern int c;

  // file2.c:
      int a;
      extern double b;
      extern int c;

____________________
  ** C  разработан  так,  чтобы  в  большинстве  случаев  позволять
осуществлять неявную  компоновку. Применение  C,  однако,  возросло
неимоверно,  поэтому   случаи,  когда  можно  использовать  неявную
линковку,  сейчас  составляют  незначительное  меньшинство.  (прим.
автора)

                             - стр 111 -

Здесь  три   ошибки:  a   определено  дважды   (int   a;   является
определением, которое  означает  int  a=0;),  b  описано  дважды  с
разными типами,  а c  описано дважды,  но не  определено. Эти  виды
ошибок (ошибки  компоновки) не  могут быть обнаружены компилятором,
который за один раз видит только один файл. Компоновщик, однако, их
обнаруживает.
  Следующая программа не является C++ программой (хотя C программой
является):

  // file1.c:
      int a;
      int f() { return a; }

  // file2.c:
      int a;
      int g() { return f(); }

  Во-первых, file2.c  не C++,  потому что  f() не  была описана,  и
поэтому  компилятор  будет  недоволен.  Во-вторых,  (когда  file2.c
фиксирован) программа  не будет скомпонована, поскольку a определно
дважды.
  Имя можно сделать локальным в файле, описав его static. Например:

  // file1.c:
      static int a = 6;
      static int f() { /* ... */ }

  // file2.c:
      static int a = 7;
      static int f() { /* ... */ }

  Поскольку каждое  a  и  f  описано  как  static,  получающаяся  в
результате программа  является правильной.  В каждом файле своя a и
своя f().
  Когда  переменные  и  функции  явно  описаны  как  static,  часть
программы легче  понять (вам  не надо  никуда больше  заглядывать).
Использование static  для  функций  может,  помимо  этого,  выгодно
влиять на расходы по вызову функции, поскольку дает оптимизирующему
компилятору более простую работу.
  Рассмотрим два файла:

  // file1.c:
      const int a = 6;
      inline int f() { /* ... */ }
      struct s { int a,b; }

  // file1.c:
      const int a = 7;
      inline int f() { /* ... */ }
      struct s { int a,b; }

  Раз правило  "ровно одно  определение" применяется  к константам,
inline-функциям и  определениям функций так же, как оно применяется
к функциям и переменным, то file1.c и file2.c не могут быть частями
одной C++  программы. Но  если это  так, то  как же два файла могут
использовать одни  и те  же типы и константы? Коротко, ответ таков:

                             - стр 112 -

типы, костанты  и т.п.  могут  определяться  столько  раз,  сколько
нужно, при  условии, что  они определяются  одинаково. Полный ответ
несколько более сложен (это объясняется в следующем разделе).

     4.3 Заголовочные Файлы

  Типы во  всех описаниях  одного и  того же  объекта  должны  быть
согласованными. Один  из способов  это достичь  мог бы  состоять  в
обеспечении средств  проверки типов  в компоновщике, но большинство
компоновщиков  -   образца  1950-х,   и  их   нельзя  изменить   по
практическим соображениям*.  Другой подход  состоит  в  обеспечении
того,  что  исходный  текст,  как  он  передается  на  рассмотрение
компилятору,  или  согласован,  или  содержит  информацию,  которая
позволяет   компилятору    обнаружить    несогласованности.    Один
несовершенный, но простой способ достичь согласованности состоит во
включении заголовочных  файлов, содержащих интерфейсную информацию,
в исходные  файлы,  в  которых  содержится  исполняемый  код  и/или
определения данных.
  Механизм включения  с помощью  #include - это чрезвычайно простое
средство обработки  текста для  сборки кусков  исходной программы в
одну единицу (файл) для ее компиляции. Директива

  #include "to_be_included"

замещает строку,  в которой  встретилось #include, содержимым файла
"to_be_included". Его содержимым должен быть исходный текст на C++,
поскольку дальше  его  будет  читать  компилятор.  Часто  включение
обрабатывается отдельной  программой, называемой  C препроцессором,
которую CC вызывает для преобразования исходного файла, который дал
программист, в  файл без  директив включения  перед тем, как начать
собственно компиляцию. В другом варианте эти директивы обрабатывает
интерфейсная система  компилятора по мере того, как они встречаются
в исходном  тексте. Если  программист хочет посмотреть на результат
директив включения, можно воспользоваться командой

  CC -E file.c

для препроцессирования файла file.c точно также, как это сделала бы
CC перед  запуском собственно  компилятора. Для включения файлов из
стандартной  директории   включения  вместо   кавычек  используются
угловые скобки < и >. Например:

  #include       // из стандартной директории включения
  #define "myheader.h"     // из текущей директории

  Использование  <>   имеет  то   преимущество,  что   в  программу
фактическое имя  директории включения не встраивается (как правило,
сначала просматривается  /usr/include/CC, а  потом usr/include).  К
сожалению, пробелы в директиве include существенны:

  #include < stream.h >    // не найдет
____________________
  * Легко  изменить один  компоновщик,  но  сделав  это  и  написав
программу, которая  зависит от  усовершенствований, как  вы  будете
переносить эту программу в другое место? (прим. автора)

                             - стр 113 -

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

 Определения типов           struct point { int x, y; }
 Описания функций            extern int strlen(const char*);
 Определения inline-функций  inline char get() { return *p++; }
 Описания данных             extern int a;
 Определения констант        const float pi = 3.141593
 Перечисления                enum bool { false, true };
 Директивы include           #include
 Определения макросов        #define Case break;case
 Комментарии                 /* проверка на конец файла */

но никогда

 Определения обычных функций  char get() { return *p++; }
 Определения данных           int a;
 Определения сложных
  константных объектов        const tbl[] = { /* ... */ }

  В системе  UNIX принято,  что заголовочные  файлы  имеют  суффикс
(расширение) .h.  Файлы, содержащие определение данных или функций,
должны   иметь    суффикс   .c.   Такие   файлы   часто   называют,
соответственно,  ".h  файлы"  и  ".c  файлы".  В  #4.7  описываются
макросы. Следует заметить, что в C++ макросы гораздо менее полезны,
чем в  C, поскольку C++ имеет такие языковые конструкции, как const
для определения  констант и inline для исключения расходов на вызов
функции.
  Причина  того,   почему   в   заголовочных   файлах   допускается
определение простых констант, но не допускается определение сложных
константных объектов,  прагматическая. В  принципе,  сложность  тут
только в  том, чтобы  сделать допустимым  дублирование  определений
переменных (даже  определения функций  можно было  бы дублировать).
Предыдущая страница Следующая страница
1 ... 13 14 15 16 17 18 19  20 21 22 23 24 25 26 ... 50
Ваша оценка:
Комментарий:
  Подпись:
(Чтобы комментарии всегда подписывались Вашим именем, можете зарегистрироваться в Клубе читателей)
  Сайт:
 
Комментарии (4)

Реклама