Бьерн Страуструп. Язык программирования С++. Специальное издание (2011) (1004033), страница 48
Текст из файла (страница 48)
Полезно локальные синонимы делать настолько локальными, насколько это возможно, чтобы избегать конфликта имен. Однако все функции синтаксического анализатора используют одни н те же наборы имен из других модулей. Поэтому мы можем поместить объявления ия1иВ непосредственно внутрь определения пространства имен Рагзег: 226 Глава 8. Пространства имен и исключения Это позволяет упростить код функций из пространства имен Рагзег практически до их первоначального состояния (86.1.1). доиЫе Рагзег:: гегт (Ьоо1 лен д умнохсение и деление ( аоиЫе 1еут=рг(т (ает) ( )ог(;; ) зыйсл (сии гол) ( сазе Еехег::МШ.: 1еуг)*= ргйп (а ие]; Ьгеааг сазе Еехег::Ш)г: 8 (г1оиЫе 4 = рпт (Ггие) ) ( 1ег) != д; Ьгеаа; ге(ига еггог ("ИЬЫе Ьу Он ); Иеуаий: гегигп 1е)У ' Я мог бы с помощью объявлений из1п8 внести в пространство имен Рагзег также и имена лексем из Еехег.
Но я оставил их полную запись с явными квалификаторами как напоминание о зависимости между Рагзег и Хехег. 8.2.3. Директивы мя(пд патезрасе Рагзег ( ИоиЫе рг(т (Ьоо1); доиЫе гегт (Ьоо!); аоиЫе ехрг (Ьоо1); иипя патезрасе Еехег; иипя патезрасе Еггог; ) У сделать все имена из Еехег доступными // сделать все имена из Еп ог доступными Это позволяет нам написать функции из пространства имен Рагзег точно так же, как мы это делали ранее (56.1.1): А что если бы мы захотели упростить функции из пространства имен Рагзег в точности до их первоначальных версий? Это бьшо бы вполне разумной задачей для большой программы, которая конвертируется из предыдущей версии с малой модульностью.
Директивы из(п8 (из(п8-йгест(ге) позволяют сделать имена из некоторых пространств имен столь же доступными, как если бы они объявлялись вне этих пространств имен (88.2.8). Например: 8.2. Пространства имен 227 ((оиЫе Рагяег: ((егт (Ьоо! ее() думножение и деление ( ((оиЫе (ей = рпт (Ле( ) ( уог(;;) янйсЬ (сигг (оа) ( саяе МИ,( 1еф *= рпт (1гие) ( Ьгеаа; саяе Р!У( (('(((оиЫе (( = рпт ((гие) ) ( (е/) l= ((( Ьгеаа; ) ге(игп еггог ( "Жг(((е Ьу 0" ); ((е(а ай: п(игп 1еу); Директивы ия1ия, примененные глобально, являются удобным инструментом для переделки готового кода (58.2.9), а в иных случаях их лучше избегать.
Эти же директивы, примененные внутри определений пространств имен, являются средством их композиции (58.2.8). В теле функций (н только там) директивы ия(пя можно смело применять с целью улучшения внешнего вида кода (58.3.3.1). 8.2.4. Множественные интерфейсы Ясно, что данное выше определение пространства имен Рагяег не является интерфейсом, предназначенным для пользователей синтаксического анализатора (иарсера).
Скорее, это набор объявлений, необходимый для удобной разработки функций синтаксического анализатора. А интерфейс для пользователя должен быть существенно более простым: патеярасе Рагяег ( ((оиЫе ехрг (Ьоо() ) К счастью, можно определить два пространства имен Рагяег, чтобы использовать каждое из ннх там, где нужно. В совокупности эти пространства имен обеспечивают две вещи: 1. Общую среду для функций парсера 2. Внешний интерфейс для пользователей парсера Так, управляющая функция табл () должна видеть лишь (У интерфейс длл пользователей патеярасе Рагяег ( ((оиЫе ехрг (Ьоо1) 228 Глава 8. Пространства имен и исключения А функции, в совокупности реализующие синтаксический анализатор, должны видеть пространство имен, составляющее интерфейс разработки калькулятора; Д интерфейс длл разработчиков патезрасе Рагзег ( аоиЫе рггт (Ьоо!); аоиЫе гегт (Ьооэ) г «(оиЫе ехрг (Ьоо!); изгое Еехег:: еег гойеп; изгое Еехег:: сиге той г из«па Еггог:: еггог; ) У испольэовать лег (о)сеп иэ Еехег У испол~эовать сиге гок из Ьехег Д использовать еггог из Еггог или в графическом вице: Рагзег ' Рагзег Реализация Рагзег Драйвер Здесь стрелки означают отношение «полагается на указанный интерфейс».
Рагзег' — это «малый» интерфейс для пользователей. Имя Рагзег' (читается «Парсер штрих») не является идентификатором С++; оно выбрано намеренно с целью подчеркнуть, что в программе для этого интерфейса отдельного уникального имени нет. Это не приводит к путанице, так как программисты привыкли давать разные имена для принципиально разных интерфейсов, и потому что физическая организация программы (59.3.2) естественным образом обеспечивает изоляцию имен (в файлах). Интерфейс для разработчиков шире такового для пользователей.
Если бы это был интерфейс разработки реальной программы большого размера, то он еше и менялся бы чаще интерфейса для пользователей. Важно, что пользователи модуля (в нашем случае функция пэагп(), использующая модуль Рагзег) изолированы от таких изменений. В принципе, нет необходимости в двух пространствах имен для отражения двух различных интерфейсов, но при желании это можно делать. Вообще, разработка интерфейса является одной из важнейших задач проектирования, в которой можно многое приобрести, и многое потерять.
Есть смысл обсудить, чего собственно мы хотим достигнуть, и есть ли альтернативные пути достижения того же самого. Предложенная нами схема решения является самой простой из всех возможных, а часто и самой лучшей. Ее основным недостатком является совпадение имен двух интерфейсов при отсутствии у компилятора достаточной информации для контроля их согласованности между собой. И даже в этом случае компилятор отслеживает многие ошибки.
Более того, заметную долю ошибок, пропущенных компилятором, отлавливает компоновщик. Предложенное решение используется далее при обсуждении физического разбиения на модули (59.3), и я его рекомендую, если нет дополнительных логических ограничений (см. также 58.2.7). 229 8.2. Пространства имен 8.2.4.1. Альтернативы интерфейсам Главная цель применения интерфейсов — уменьшение зависимостей между частями программы.
Минимально достаточные интерфейсы приводят к программам, которые легко понять, которые хорошо скрывают внутренние данные, которые легко модернизировать, и которые даже компилируются быстрее. Следует иметь в виду, что в отношении зависимостей и компиляторам, и программистам часто свойственен упрощенный взгляд на проблему; «Если определение видимо в точке Х, то любой код в точке Х зависит от содержимого этого определения». В общем случае, однако, дела обстоят не столь плохо, поскольку на самом деле большая часть кода не зависит от большей части определений.
К примеру, в нашем случае мы имеем следующее: У интерфейс длл реализации аатезрасе Рагзег ( уу ... ИоиЫе ехрг (Ьоо(); уу ... ) Ы( та(и ( ) У... Рагзег:: ехрг Царе); УУ... ) Здесь функция а»ага () зависит только от Рагзег:: ехрг (), но чтобы выявить этот факт, нужно время, умственные усилия, практическая проверка гипотезы и т.д. Как следствие, в реальных программах больших размеров и компиляторы, и программисты перестраховываются и считают, что если зависимость может быть, то она действительно имеет место. Надо сказать, этот подход разумный. Таким образом, желательно организовать программу так, чтобы уменьшить набор потенциальных зависимостей до набора реальных зависимостей. Сначала пробуем очевидное: определяем пользовательский интерфейс к парсеру в терминах имеющегося интерфейса разработчика: УУ интерфейс длл реализации иатезрасе Рагзег ( У... ИоиЫе ехрг (Ьоо!); уу ... ) иатезрасе Рагзег Гите«тасе ( У/ интерфейс длл пользователей ( имия Рагзег:: ехрг; ) Ясно, что пользователи Раглег (агегуасе зависят только (причем косвенно) от Рагзег:: ехрг () .
Однако на первый (поверхностный) взгляд имеет место следующая схема зависимостей: 2ЗО Глава 8. Пространства имен и исключения Рагзег Рагзег Д Теперь уже кажется, что пользователь (управляющая программа) зависит от любых изменений в Рагзег, от которых мы его хотели бы изолировать. Даже такая кажущаяся зависимость нежелательна, и мы явным образом ограничиваем Рагзег Ьпгег/асе лишь имеющей отношение к пользователям частью интерфейса Рагзег (ранее мы это называли Рагзег'): Д интерфейс длн пользователей патезрасе Рагзег ( аоиЫе ехр«(Ьоог) ) патезрасе Рагзег (псе«Гасе У отдельно поименованный интерфейс длл пользователей иапд Рвгзег:: ехрг; ) нлн графически: Рагзег' Рагзег Рагзег (нее«гасе Реализация Рагзег~ Драйвер патезрасе Расее~ ииегуасе ( аоиЫе ехрг (Ьоо!); Теперь нет необходимости держать Рагзег в области видимости в момент определения Рагзег (изег/асс.
Это нужно лишь в момент определения функции Рагзег гите«тасе:: екрг ( ): Для проверки согласованности Рагзег и Рагзег' между собой мы снова полагаемся на совокупность всех средств компиляции и компоновки, а не на работу одного лишь компилятора над одной единицей компиляции. Настоящее решение отличается от такового из 58.2.4 лишь наличием дополнительного пространства имен Рагзег гиге«~асе.
Прн желании мы могли бы явным образом обьявить функцию ехрг() непосредственно в Рагзег (иге~/асе: 8.2. Пространства имен аоиЫе Рагеег 1пгегуасе:: ехрг (Ьоог аег) ( ге(ига Рагеег:: ехрг (Лен; ) Последний вариант можно отобразить графически следуюшим образом: Рагеег 1пгегуасе Рагеег Все зависимости теперь сведены к минимуму, все конкретизировано и именовано надлежашим образом. Однако в большинстве реальных практических случаев, с которыми мне приходится сталкиваться, зто решение чрезмерно сложно и выходит за пределы необходимого. 8.2.5. Как избежать конфликта имен Пространства имен предназначены лля отражения внутренней структуры кода. Простейшей целью применения пространств имен является разграничение кода, написанного одним программистом, от кода, написанного кем-то еше.
Такое разграничение кода имеет огромное практическое значение. Действительно, на базе одной лишь глобальной области видимости неоправданно сложно составлять программу из отдельных частей. Проблема состоит в том, что предположительно отдельные части могут определять одни и те же имена.
При объединении частей в программу происходит конфликт имен. Рассмотрим пример: УУ ту.й: сйаг у'( сйаг); ими'(1пг); с1аее ЯР1пд ( У* ... *У ); УУуоиг.й: сйагу'( сйаг); аоиЫе у ( ИоиЫе); с1аее Ягг(пя ( У* ... *I ); При наличии таких определений трудно составить единую программу, используя одновременно и пту.й, и уоиг.й. Очевидное решение состоит в том, чтобы включить каждый из наборов объявлений в его собственное пространство имен: пагоеерасе Му ( сйаг у'( сйаг); Ыг/ (1пг) г с)аееЯ(г1пд (У* ... *У); Глава 8.
Пространства имен и исключения 252 патеврасе оиг ( сйаг/ (сйаг) г йоиЫе/'(йоиЫе) / с1ат Я~г/пд ( /* ... */ ) ' ) Теперь можно использовать объявления из Му и Уоиг либо при помощи явной квалификации ($8.2.)), либо применением объявлений аппп (88.2.2) или директив ивта (88.2.3). 8,2.5.1. Неименованные пространства имен Часто бывает полезно обернуть набор объявлений объемлющим пространством имен просто ради того, чтобы избежать потенциального конфликта имен. В таком случае главная цель состоит в локализации имен, а не в предоставлении интерфейса к пользовательскому коду. Например: ()1пс!иае "Йеааег. Й'" патеврасе Мого ( /пг а; гоЫ г"() ( /* ...