Б. Страуструп - Дизайн и Эволюция C++. 2006 (1160775), страница 99
Текст из файла (страница 99)
недостаточно, так как при этом не меняется имя, доступное компоновщику. 17.2Л. Обходные пути Существует несколько обходных путей. Например: // яу.Ы онат ву 1(сват); ИИИИИИИБ Для чего нужны пространства имен Бпс пу г(тпг); с1авв ву Бсг1пд ( /* ... */ ); // уоиг'.Ь сЬаг уо г(спаг); ооиЫе уо г (с)оиЫе!; с1авв уо Бсггпд ( /* ... */ Такой подход не является необычным, но он совершенно неудачен, а если префиксы к тому же не очень короткие, то и неудобен для пользователя. Другая сложность состоит в том, что существует лишь несколько сотен двухбуквенных префиксов, а на С++ уже написаны сотни библиотек. Это одна из самых старых проблем из числа рассматриваемых в данной книге. Программисты, давно работающие на С, вспомнят то время, когда в имена членов структур включались одноили двубуквенные суффиксы, чтобы избежать конфликтов с членами других структур. Использование макросов может сделать такое решение еще хуже (или лучше, если вам макросы нравятся): // пу.Ь: ()оегтпе пу(Х) аургет1х а()Х спаг пу(г) (сЬаг); ау(Е) (1пг); с1авв пу(зггтпд) ( /* ...
*/ // уоиг.Ь ()оег1пе уо(х) уоиг тих сЬаг уо(2)(сЬаг); ооиЫе уо ( г ) (йоиЫе); с1авв уо(Бггхпд) ( /* ... */ ); // ау.Ь: с1авв Му ( риЫ1с: всагтс сЬаг г(спаг); вгагтс 1пг г(1пг); с1авв Бсг1пд ( /* ... */ ); ): Идея состоит в том, чтобы разрешить длинные префиксы в именах, видимых компоновщику, оставив короткими имена, используемые в исходном тексте. Как и все схемы, основанные на макросах, зта также создает проблемы для инструментальных средств: отслеживать отображение имен должен либо инструмент (за счет существенного усложнения), либо пользователь (что усложняет программирование и сопровождение). Альтернативный подход — его обычно предпочитают те, кто не любит макросы, — поместить всю необходимую информацию внутри класса: ЕИИИИИИН Пространства имен // уоиг.)1 с1аяя уоиг ( рио1ьс: ясагтс сааг г(сяаг); ягастс соис1е я (соия1е); с1аяя Яггьпя ( /* ... */ ); ); 17.3.
Какое решение было бы лучшим? Для решения проблем, связанных с пространством имен, можно воспользоваться разными механизмами. На самом деле, во многих языках имеются, по крайней мере, зачатки средств такого рода. Например, в С есть статические функции, в Разса1 — вложенные области действия, в С++ — классы, но более полные решения следует искать в таких языках, как Р1./1, Ада, Мо()ц!а-2, Моди!а-3 [Хе!яоп, 19911, М). ['1)(/)1)(яггош, 1987) и С1.ОЯ [К)яга1ея, 1992[.
Итак, что должен был бы дать языку С++ хороший механизм пространств имен? Продолжительная дискуссия рабочей группы по расширениям при комитете А1ч81/'180 позволила составить целый список преимуществ. Вот эти возможности: о связь с двумя библиотеками без конфликтов имен; о введение имени без конфликта с чужими именами (например, из библиотеки, о которой я никогда не слышал, или неизвестными мне именами из библиотеки, которую, как мне казалось, я знаю); о добавление новое имя в библиотеку без ущерба интересов пользователей; о использование имен из двух разных библиотек, даже если эти имена совпадают; о разрешение конфликтов имен без модификаций самих функций (только за счет манипулирования разрешением имен на уровне объявлений); о добавление нового имени в пространство имен, не опасаясь вызвать незаметного для пользователя изменения смысла кода, использующего другие пространства имен (мы не можем дать таких гарантий для кода, где используется пространство имен, в которое добавлено имя); о отсутствие конфликтов между именами самих пространств имен; в частности, реальное (видимое компоновщику) имя может быть длиннее имени, использованного в тексте программы; (з применение механизма пространств к стандартным библиотекам; о совместимость с С и С++; о отсутствие издержек на этапе компоновки и во время выполнения при использовании пространств имен; К сожалению, и этот подход не лишен многих мелких неудобств.
Не все глобальные объявления можно так просто перенести в класс, а некоторые от этого меняют семантику. Например, чтобы избежать изменений семантики, глобальные функции и переменные должны объявляться как статические члены, а тела функций и инициализаторы обычно следует отделять от объявлений.
Какое решение было бы лучшим? ИИИИИВИИ о лаконичность пространств имен, не меньшая, чем при использовании глобальных имен; о явное указание в коде, из какого пространства должно быть взято имя. Хорошее решение также должно быть простым. Под простотой я понимаю следующее: о чтобы механизм можно было сразу использовать для серьезных задач, на объяснение его работы должно уйти не более десяти минут. Чтобы объяснение удовлетворило языковых пуристов, может потребоваться гораздо больше времени; о разработчик компилятора должен быть способен реализовать этот механизм менее чем за две недели. Разумеется, простоту здесь нельзя понимать строго, ведь людям с разной подготовкой и способностями для усвоения одного и того же материала требуется неодинаковое время.
Есть также несколько свойств, которые мы сознательно решили исключить из списка критериев, хотя об их включении часто просили: о возможность взять два объектных файла с конфликтующими именами и связать их вместе. Это могут сделать инструментальные средства в любой системе, но неясно, как можно было бы реализовать для этого языковую поддержку без значительных затрат.
Существует слишком много компоновщиков и форматов объектных файлов, чтобы рассчитывать на их приведение к общему стандарту. Чтобы решение было полезным для С++, оно должно зависеть только от возможностей, предоставляемых практически всеми современными компоновщиками; о возможность назначать произвольные синонимы именам, используемым в библиотеках.
Существующие механизмы — куреней, ссылки и макросы— позволяют это делать в некоторых ситуациях, а универсальным средствам переименования я не доверяю (сы. раздел 12.8). Отсюда следует, что механизм устранения неоднозначности должен быть встроен в объектный код авторами отдельных фрагментов программы. В частности, поставщикам библиотек придется использовать какой-то метод, который дал бы возможность пользователям разрешить неоднозначность.
К счастью, именно поставщики библиотек больше всего выигрывают от систематического использования пространств имен. Разумеется, этот перечень можно продолжать, и многие наверняка не согласны с оценкой относительной важности каждого критерия. Тем не менее список дает представление о сложности задачи и о требованиях, которым должно удовлетворять решение. Изложив все критерии, я получил возможность проверить, в какой мере проектирование пространств имен отвечает требованиям простоты.
Питер Джуль выполнил первую реализацию за пять дней, а мне менее чем за десять минут удалось объяснить основы механизма пространств имен нескольким пользователям. Заданные вопросы показали, что мои «ученики» усвоили суть и смогли самостоятельно ЙЗИИИИИН Пространства имен додуматься до таких применений пространств имен, которые я вообще не объяс- нял.
Итак, механизм оказался достаточно простым. Опыт последующей реализа- ции, обсуждение концепции пространств имен и некоторые их приложения утвер- дили меня в этом мнении. 17.4. Решение: пространства имен Принятое решение в принципе очень просто. Оно дает четыре новых механизма: о механизм определения области действия, обобщающий глобальные объявления в С н С++: пространства имен. Такие области действия разрешается именовать, а к их членам можно обращаться с помощью обычного для членов класса синтаксиса: патезрасе пате::тевЬег пате, где павезрасе лаве— имя пространства имен, а шевЬех паше — имя члена. Фактически область действия класса можно считать частным случаем области действия пространства имен; о механизм определения локального синонима для имени из пространства имен; (з механизм, позволяющий обратиться к одному члену пространства имен, не указывая имя пространства имен, то есть опуская префикс павезрасе паве:: . Это достигается использованием пвшя-объявления; а механизм, позволяющий обращаться ко всем членам пространства имен, не указывая явно имя самого пространства имен.
Достигается использованием ця1пя-директивы. Тем самым удовлетворяются все критерии, приведенные в разделе 17.3. Кроме того, решается давний вопрос о том, как обратиться к членам базового класса из области действия производного класса (см. разделы 17.5.1 и 17.5.2). Заодно становится избыточным атрибут зсасйс для глобальных имен (см. раздел 17.5.3). Рассмотрим пример: патеврасе А ( чо1о 1(1пс); чога 1(спас); с1ввв Ясг1пд ( /* ° ° ° */ )~ // ) А::ЯШпд в1 = "Аппеваг1е" / чо1й д1() ( А::1(1); ) Имена, объявленные внутри фигурных скобок, принадлежат пространству имен А и не конфликтуют ни с глобальными именами, ни с именами из других пространств имен. Объявления внутри пространства имен (в том числе, и определения) имеют в точности ту же семантику, что и глобальные объявления, но их область действия ограничена этим пространством имен.
Программист может использовать такие имена, добавляя явный квалификатор: 061ИИИФЕП Решение. "пространства имен Вместо этого можно явно указать, что использование квалификатора является излишним для отдельного имени из конкретного пространства имен. Для этого служит цв1пя-объявление: пвгпд А::Явг1пд; ягв1пд в2 = "н1спо1ав"; // то же, что А::ясггпд ноЫ д2 () ( пв(пд А::Г; г(2); // вводим локальный синоним длн Г иэ А // то же, что А::Г // сделать доступными все имена иэ А // то же, что А::вЗ ивьпд патеврасе А; Ясгхпд вЗ = "Маг1ап"; но1с дЗ() ( Г(З); // то же, что А::Г ) Ув)пя-директива не вводит имена из указанного пространства имен в локальную область действия, а просто делает их доступными.