Бьерн Страуструп. Язык программирования С++. Специальное издание (2011) (1004033), страница 15
Текст из файла (страница 15)
Массивы индексируются с нуля, то есть в содержит следующие десять элементов: в(0] ... г(9) . Указатель может содержать адрес объекта соответствующего типа: р = Ьг(3]; //р указывает на четвертый элемент массива г Унарная операция дг означает «взятие адреса». Рассмотрим копирование десяти элементов массива в другой массив: гоЫ енот)эег Гавот)оп ( ) ( (нг гг (10) з Гпт г()0); твг()пт.тдз <грз -Л г)(з]=.г(э')) ) Оператор цикла/ог задает следующую последовательность действий «присвоить з значение 0 пока г' меньше 10, копировать гъый элемент и инкрементировать ь. Операция инкремента ++ увеличивает значение целой переменной на единицу. 2.4. Модульное программирование С течением времени акцент в разработке программ сместился от разработки процедур в сторону организации данных. Помимо прочего, этот сдвиг вызван увеличением размера программ.
Набор связанных процедур и обрабатываемых ими данных часто называют модулем (тода!е). Парадигма программирования теперь звучит так: Решите, какие модули нужны; сегментируйте программы так, чтобыданные были скрыты в модулях Эта парадигма известна также как принцип сокрытия данн»эх. В задачах, где группировка процедур и данных не нужна, процедурный стиль программирования по-прежнему актуален. К тому же, принципы построения «хороших процедур» теперь применяются по отношению к отдельным процедурам модуля. Типовым примером модуля может служить определение стека.
Вот соответствующий список задач, требующих решения; 1. Предоставить пользовательский интерфейс к стеку (например, функции ривй() и рор() ). 2. Гарантировать доступ к внутренней структуре стека (реализуемой, например, с помощью массива элементов) исключительно через интерфейс пользователя.
3. Гарантировать автоматическую инициализацию стека до первого обращения пользователя. Глава 2. Обзор языка С++ 66 Язык С++ предоставляет механизм группировки связанных данных, функций и т.д, в пространства имен (патезрасез). Например, пользовательский интерфейс модуля отасЬ может быть объявлен и использован следующим образом: патезрасе озава У интерфейс ( гоЫ резЬ (слог); слог рор (); ) гоЫг"() озава::ризЬ ( 'с' ); (Г(8(асЬ::рор() ! = 'с' ) еггог( Итрозз(О(е"); ) Кволификатор огасЬ:: означает, что речь идет о функциях ризЬ () и рор (), определенных в пространстве имен огасЬ. Иное использование этих имен не конфликтует с указанным и не вносит никакой путаницы в программу. Полное определение стека можно разместить в отдельно компилируемой части программы: патезрасе Игаса 0 реализация ( сопя( тг тах зйе= 200; слог( г [тах з(зе); (и(гор = 0; гоЫ ризЬ (сьаг с) ( У* проверить на переполнение и риза с *У ) слог рор() (»»* проверить на пустоту стека ирор *»» ) Ключевым в этом определении является тот факт, что пользовательский код изолируется от внутреннего представления модуля атас/с посредством кода, реализующего функции атас/с::ризЬ () и отасЬ::рор () .
Пользователю нет необходимости знать, что огасЬ основан на массиве, и в результате имеется возможность изменять его внутреннее представление без последствий для пользовательского кода. Символы 2* маркируют начало в общем случае многострочного комментария, заканчивающегося символами *!.
Так как данные есть лишь часть того, что бывает желательным «скрыть», то концепция сокрытия данных очевидным образом расширяется до концепции сокрытия информации: имена функций, типы и т.д. локально определяются внутри модулей. Для этого язык С++ разрешает поместить в пространство имен любое объявление (88.2). Модуль огасЬ является лишь одной из возможных реализаций стека. Иные примеры стека в последующих главах будут иллюстрировать другие стили программирования. бу 2.4 Модульное программирование 2.4.1. Раздельная компиляция Язык С++ полдерживает принцип раздельной компиляции (зерага!е сотр!!о!/оп), взятый из языка С.
Это позволяет организовать программу в виде набора относительно независимых фрагментов. В типовом случае, мы размещаем части модуля, ответственные за интерфейс взаимодействия с пользователем, в отдельный файл с »говорящим» именем. Так, код патезрасе у!асй // интерфейс ( го(д ривй (сйаг); сйаг рор (); ) будет помещен в файл к!асй.й, называемый заголовочным файлом (йеодег/)!е). Пользователи могут включить его в свой код следующим образом: Ь/пс(гите "Маей.й" //включить интерфейс го!д/'() о(асй::ривй ( ' с ' ); (/(о(асй::рор() ! = 'с' ) еггог("!травя!Ыв"); ) Чтобы компилятор мог гарантировать согласованность кода, в файл с реализацией стека также нужно включить интерфейсный заголовочный файл: ()!пс/иде "з!асй.й" //включить интерфейс патввврасе 5(асй // представление ( сопл! (и( тах з(ее= 200; сйаг г(тах в(св); (и! (ор = Ог ) гоЫ 5!асй::ривй (айаг с) ( /* проверить но переполнение и ризй с */ ) сйаг,з!асй::рор () ( /* проверить но пустоту стека и рор */ ) Пользовательский код может располагаться в третьем файле, скажем, изег.с.
Код файлов ивег.с и згасй.с разделяет информацию об интерфейсе стека, расположенную в файле в!пей.й. Во всем остальном файлы изег.с и втасй.с независимые и могут компилироваться раздельно. Графически, рассмотренные фрагменты программы можно представить следующим образом; лгос/сй: вга ивег.с.' Ыпс/иИв "ма использовать Глава 2. Обзор языка С++ 68 Раздельная компиляция является чрезвычайно полезным инструментом в реальном программировании.
Это не узкая материя, с которой сталкиваются лишь программы, реализующие в виде модулей концепции стека и т.п. И хотя раздельная компиляция н не относится, строго говоря, к базовым элементам языка, она наилучшим образом позволяет извлечь максимальную пользу из его конкретных реализаций.
Так или иначе, это вопрос большого практического значения. Целесообразно стремиться к достижению максимальной степени модульности программы, логической реализации этой модульности в конструкциях языка, и физической раскладке модулей по файлам для эффективной раздельной компиляции (главы 8, 9). 2.4.2.
Обработка исключений Когда программа выполнена в виде набора модулей, обработка ошибок должна рассматриваться в контексте модулей. Какой модуль отвечает за обработку тех или иных ошибок? Часто модуль, столкнувшийся с ошибочной ситуацией, не знает, что нужно предпринять для ее исправления. Ответные действия зависят от модуля, инициировавшего операцию, приведшую к ошибке, а не от модуля, выполнявшего эти действия. По мере роста размера программ и в связи с интенсивным использованием библиотек, стандарты по обработке ошибок (или шире — «исключительных ситуаций») становятся важными.
Снова рассмотрим пример с модулем агава. Что следует предпринять в ответ на попытку занести в стек слишком много символов? Автор модуля агава не знает, что нужно пользователю в этом случае, а пользователь не может распознать ситуацию переполнения стека (в противном случае он ее вообще не допустил бы). Устранить указанное противоречие можно следующим образом: автор модуля агава выявляет переполнение стека и сообщает об этом (неизвестному) пользователю. В ответ пользователь предпринимает необходимые действия. Например: р интерфейс пателрасе 8(асй ( еоЫ риля (сйаг) г сйаг рор ( ); с(авв Оиегг)о»«( ); ) ,(г тип, представляющий исключение, связанное с переполнением Обнаружив переполнение, функция Угаси::рдей () может косвенно вызвать код обработки данной исключительной ситуации, сгенерировав «исключение типа О«сгнои»»: гоЫ згасй::риля (сйаг с) ( (Т(гор == так зае) ей«от Огег/$от() рпоместить с в стек ) Оператор га«ом передает управление обработчику исключений типа отава:: Ого«ггом, расположенному в теле функции, прямо или косвенно вызвавшей функцию Яасй::раза () .
Для этого компилятор самостоятельно создает машинный код, роскручивоющий стен вызовов функций назад до состояния, актуального на момент работы указанной функции. Таким образом, оператор га«ою работает как многоуровневый оператор «ега«а. Вот соответствующий пример; 2.5. Абстракция данных зоЫ )"() ( уу ... ау УУ возникающие здесь исключения передаются обработчику, ояредсленному ниже ( юьбе (гсие) Я(иск::риза ( ' с ' ]; ) са!сй (Я~оса:: Очебтою) ( УУоорз: первполнение стека; предпримем надлежащие действия ) уу ...
Здесь цикл иазте в ггу-блоке пытается выполняться вечно. В результате, при некотором обращении к функции бгаск::риза () последняя сгенерирует исключение типа агаси:: Оивггзою, и управление будет передано в сагой-блок, предназначенный для обработки исключений зтого типа. Использование механизма исключительных ситуаций языка С++ позволяет сделать обработку ошибок более регулярной, а тексты программ более кчитабельными». Более подробно этот вопрос рассматривается в ВЗ.З, главе 14 и приложении Е.