Б. Страуструп - Язык программирования С++. Специальное издание, 3-изд. Бином. 2004 (1160791), страница 47
Текст из файла (страница 47)
Если передана нулевая строка, кодирование не осугцествляется. (*3.5) Напишите программу дешифровки сообщений, закодированных методом, описанным в 9 7.10[9], не зная кпоча. Подсказка: см. )9ашг) Ка!ш: ТЬе Сел?ебгга/сень Масш!!)ап, 1967, )л)елл Ъ"ог)с рр. 207 — 213. (*3) Напппппте функцию еггог, получалощую рггп(1-подобную строку форл~атп- роваппя, содержащукл дцрективы %з, %с н %с(, и произвольное количество дру- гих аргументов. Не пользуйтесь функцией рг(п(Ц. Если вы не знаете смысл зтпх директив, обратитесь к 9 21.8. Используйте <сзМагд>. (*1) Как бы вы выбирали имена для типов указателей на функгши, определяемых с помощью 1уреглеу? (*2) Просмотрите несколько програмлц обращая внимание на разницу стилей при выборе имен.
Как используются заглавные буквы? Как используется символ подчеркивания? Когда используются короткие имена, такие как )и к? (*1) Что плохо в следующих макро-определениях? (*3) Напишите простой макро-процессор (наподобие препроцессора С). Счптывайте пз с?п и выводите в соиб В начале не обрабатывайте макросы с аргументами. Подсказка: В калькуляторе (9 6.1) имеется таблица символов и лексический анализатор, которые вы можете модифицировать.
(*2) Реализупте рПп1 [) из 9 7.5. ('2) Добавьте функции типа здг( !), 1олл [) и ип !) к калькулятору из 9 6.1. Подсказка: предопределите зтп имена и вызывайте функции при помощи массива указателей на функпии. Не забывайте проверять аргументы перед вызовом функции. (*1) Напишите функцшо вычисления факториала, не использующую рекурсикл См.
также 6 11.14[6]. ('2) Напишите функции, которые добавляют один день, один месяц и один гол к Ра1е, определенной в 9 5.9[13]. Напишите функцию, которая возвращает лень недели для заданной даты. Напишите функцию, которая возвращает дату первог о понедельника, следующего за заданной датой. Пространства имен и исключения Год 787г От Ролсдвоова Христово? — Монти Пайтон Не бывает правил бвз исключений. — Роберт Бартон Модульность, интерфейсы и гюключения — пространства имен — азгпу — изгой аатеврасв — разрешение конфликтов имен — поиск имен — композиция пространств имен — псевдонимы пространств имен — пространства пмсн и код на С вЂ” исключения — йгош н со~со — искл1очения и структура программы— советы — упражнения.
8.1. Разбиение на модули и интерфейсы Любая реыьная п рограмма состоит нз некоторого количества отдельных частей. Например, даже простая программа, такая как Здравствуй, мир!, состоит из по крайней мере двух частей: пользовательский код требует вывести Здравствуй, мир!, а система ввода/вывода непосредственно осуществляет вывод. Рассмотрим пример калькулятора из ~ 6.1, Можно считать, что оп состоит нз пяти частей: [11 Обработчика, выполняющего синтаксический анализ. [21 Лексического анализатора, выделяющего лексемы из последовательности символов.
[3~ Таблицы символов, в которой хранятся пары (строка. значение). [4) Управляющей программы (драйвера) та)п (). [51 Обработчика ошибок. Это можно изобразить графически: обработчик ошибок 210 Глава 8. Пространства имен и исключения Стрелки означают «использует>к С целгпо упрощения схемы, я не отобразил тот факт, что каждая часть пользуется обработчиком ошибок. В действительности, калькулятор был задуман в виде трех частей, а драйвер и обработчик ошибок были добавлены впоследствиии для полноты картины.
Когда один модуль пользуется другим, ему пе надо знать все подробности об используемом модуле. В идеале болыцая часть деталей реализации модуля должна быть неизвестна его пользователям. Следовательно, мы проводим различие между модулем п его интерфейсом. Е1апрпмер, синтаксический анализатор обращается только к интерфейсу лексического анализатора, а не к его реализапнгь Лексический анализатор просто реализует функции, обьявленные в его интерфейсе. Это можно изобразить графически следующим образом: го таблицы символов символов брб «б > Пунктирные линии означают «реализует >.
Я полагаю, что приведенная схема реально отражает структуру нашей программы, и наша работа в качестве программистов состоит в том, чтобы добросовестно отразить это в коде. Тогда код будет простым, эффективным, понятным, легким в сопровождении и т. л., потому что он будет непосредственно отражать имеющийся проект. В последующих разделах показано, как можно сделать более простой и понятной логическую структуру калькулятора, а в й 93 описывается, как физически организовать псхолные тексты программ в удобном анде.
Калькулятор — крошечная программа п в «реальной жизни> я бы не стал пользоваться механизмом пространств имен и раздельной компиляцией 19 2.4.1, 9 9.1) в той мере, в какой я это лелаю здесь. Речь идет о демонстрации методов, полезных прп написании больших программ.
В реальных программах каждьш «модуль», представленный в виде отдельного пространства имен, содержал бы сотни функций, классов, шаблонов и т. д. Для демонстрации различных методов н средств языка разбиение на модули проводится по этапам. В «реальной жизни > вряд ли были бы пройдены все эти этапы. Опытный программист с самого начала построил бы систему «почти правилыюм Олнако по мере развития программы ее структура нередко кардинально меняется.
Обработка ошибок используется во всех местах программы. При разбиении программы на модули, плп наоборот, при сборке программы из модулей, мы должны заботитьгя о минимизации зависимостей между модулямп. возникающих из-за обработки г(1 8.2. Пространства имен 8.2. Пространства имен Пространство имен является механизмом отражения логического группирования.
То есть если некоторые объявления можно объединить по какому-либо критерию, пх можно поместить в одно пространство имен для отражения этого факта. Например, объявления синтаксического анализатора для калькулятора Ц 6.1.1) можно поместить в просгранство имен Рагяег: патеярасе Рагяег( 1(оиб!е ехрг (боой, с(оиб!е рг!т (Ьоо! де1) ( (" .. '/) с!оиЫе 1егт (Ьоо!Ре1)(1*... *!) г!оиЫеехрг(боо!дог) ( 1'*" "!) гвункцпя ехрг () должна быть сначала объявлена и только затем определена для того, чтобы разорвать замкнутый круг зависимостей, описанный в з 6.1.1. Часть настольного калькулятора, отвечающую за ввод, тоже можно поместить в собственное пространство имен.
патсярасе Еехег ( спшп Тобеп иа!ие ( МАМЕ, РЕЕ18=+', РРТД!Т=';, й!ЮМаЕР, М1!У(!8 =' — ', АЛИБИ='=', ЕЬ!О, МЕЕ='*', ЕР=' (', Тобеп иа!ие сигг 1од; с!оиЫе питЬег иа!ис; яЫ::я1г1пдягг!пд оа!ие, Тодеп ои1ие де1 1обеп () (!'... 'У) Нодобное использование пространства имен делает более очевидным что, собственно, синтаксический и лексический анализаторы предоставляют своим пользователям. ошибок.
В С - ь имеется механизм обработки исключений. которьш позволяют отделить процесс обнару кения ошибок и сообщения об ошибках от непосредственной их обработки. Поэтому обсуждение представления модулей как пространств имен (Ь 8.2) сопровождается демонстрацией того, как моя<но воспользоваться исключениями для дальнейшего улучшения модульности (8 8.3) программы. Существует гораздо больше понятий модульности, чем обсуждается в этой и следующей главах. Например, для демонстрации важных аспектов модульности можно было бы воспользоваться параллельно работающими процессами, которые обмениваются между собой сообщениями.
Аналогично, важными вопросами, не обсуждаемыми здесь, являются использование раздельных адресных пространств и обмен информацией между ними. Я полагаю, что зти вопросы, связанные с модульностью, довольно независимы и находятся в разных плоскостях. Любопытно, что в любом случае разбиение систсмы на модули реализуется довольно просто. Серьезная проблема — обеспечить безопасное, удобное и эффективное взаимодействие модулей. Глава 8. Пространства имен и исключения 212 и ат евра се Рагвег ( с(оиЫе ргип (Ьоо! шеф с(пиЫе !епп !Ьоо1 де!) с!оиЫе ехрг (Ьоо1 де!!, с(оиЬ!е Рагяег рпт ~Ьоо(де() ! (* ...
'( ) с(о иЫе Ра геегк(ест (Ьоо! де!) ! ('* ... '(' ) с(оиЫе Рагвег:ехрг !Ьоо! де!! ! 1' ... *>> ) Обратнте внимание, что в результате разделения реализации п интерфейса, каждая функция имеет ровно одно объявление и одно определение. Пользователи увидят только интерфейс, содержащий объявления. Реализация (в нашем случае — тела функ ппй) будет находиться «где-то в другом местев, куда пользователю пег необходимости заглядывать. Как показано в примере, член пространства имен можно объявить внутри определения пространства имен, а определить позднее, прн помощи следующей записи: или> протпронства ииенсимя члена. Члены пространства имен объявляются следующим образом; птпеврисе им» простринства имен ! (( объявления и оправ>ения Нельзя объявить новый член пространства имен вне его опредеяения, используя яв- ный квалификатор. Например: Оотибка: в Ршхегнет !орса!() оо!бРагвек;!од!са! ~Ьоой; Идея состоит в том, чтобы достаточно легко можно было бы найти все имена в объяв- лении пространства имен и быстро выявить такие ошибки, как опечатки н несоответ- ствие типов.
Например: l(отноко: в Рагяегнет Ьет() (( ошибки: у Рв тегн р т () аргу>к нп 6оо! с!оиЬ!е Рагяег, !гет (6оо!) г!оиЫе Рагвег:рг!т !!и!); Пространство имен является областьк> видимости. Поэтому «пространство именя является фундамсцтальной н относительно простой концепцией. Чем больше программа, тем полезнее становятся пространства имен для адекватного отражения логического разделения ее частей.
Обьпщыс локальные и глобальные области видимости и классы являются пространствами имен (й ВПО.З). Однако если бы я добавил сюда исходные тексты функций, эта структура не выглядела бы такой стройной. Тэм, где тела функций включены в объявления пространства имен реального размера, вам пришлось бы пробиваться сквозь страницы кода для того, чтобы определить, как!ле функции предлагаются, плп, другими словами, для того, чтобы понять интерфейс. Альтернативой отдельно описанным интерфейсам может служить некоторый инструмент, который извлекает интерфейс из модуля, содеря<ащего детали реализации. Я нс считаю это хорошим решением.