Б. Страуструп - Дизайн и Эволюция C++. 2006 (1160775), страница 101
Текст из файла (страница 101)
Пространства имен можно применять для составления интерфейсов, в которые входят имена, принадлежащие сразу нескольким пространствам: ИИИИИИИВ Пространства имен чШиа1 чоЫ 11() = О; чгггиа1 чоЫ г2() = О; // ): // ) 1вр1 — это некоторое пространство имен, в котором находятся детали реализации. Пользовательский код мог бы выглядеть так: с1авв ХХ : риЫ1с ге1еаве1::х ( 1пг хх1; // риЫ1с: чоЫ г1(); чоЫ (2(); чггсиа1 чоЫ Г(1(); ч1гтаа1 чоЫ И2(); // ) паиеврасе ге1еаве1 ( // с1авв Х ( 1ир1::Хгер* р; // версия 1 оставлена для совместимости // 1ир11::Хгер приведена в соответствии // с версией 2 риЫ1с: ч1ггпа1 чоЫ г1() = О; ч(ггпа1 чоЫ (2() = О; // ); // Это значит, что поставщик библиотеки не может изменить размер объектов класса ге1еаве1:: Х (например, добавить новые данные-члены), добавить или изменить порядок следования виртуальных функций и т.д., поскольку это потребовало бы перекомпиляции пользовательского кода, чтобы учесть новое размещение объекта в памяти.
Существуют реализации С++, защищающие пользователя от таких изменений, но пока не получившие широкого распространения, так что разработчик библиотеки не может полагаться на них, если не хочет привязать себя к единственному компилятору. Я мог бы посоветовать пользователям не наследовать подобным библиотечным классам таким образом, но ведь, несмотря на все предупреждения, они все равно будут поступать по-своему и жаловаться на необходимость перекомпиляции. Нужно найти другое, более удачное решение.
Если воспользоваться пространствами имен для различения версий, вторая версия ге1еаве2 могла бы выглядеть так: (звиииееие Решение: пространства имен панезрасе ге1еаяе2 ( // с1авв Х ( 1ир1::ХгеР* Р; рпвгсс: чггсиа1 чоЫ 12( чсгспа1 чоЫ ЕЗ( ч1гсиа1 чоЫ Е1( // // новый порядок следования // новая функция ) =О) ) =О) ) = О; // ) может инкапсулировать все зависимости от версии и использоваться всюду: З(пс1пде "11Ь.Ь" о1аяв ХХ : рпЬ11с 11Ь::Х ( // ): При переходе на новую версию достаточно изменить лишь одну строку: // 11Ь.Ь: панезрасе 11Ь = ге1еаве2; // Такое изменение делается лишь тогда, когда появились основательные причины перейти к версии ге1еаяе2, и нашлось время, чтобы перекомпилировать программу и разобраться с возможными несовместимостями между обеими версиями на уровне исходных текстов.
17А.5. Технические детапи Здесь рассматриваются технические детали, касающиеся разрешения областей действия, глобальной области действия, перегрузки, вложенных пространств имен и составления пространства имен из отдельных частей. 174.$.1. Удобство н безопасность 1)я(пя-объявление добавляет имена в локальную область действия.
Пз(пя-директива этого не делает, она лишь обеспечивает доступность имен. Например: Старый код пользуется версией ге1еаяе1, новый — версией ге1еаве2. При этом оба кода работают и не вступают в конфликт. Заголовочные файлы для версий ге1еаяе1 и ге1еаяе2 различаются, так что для обеспечения минимальной функциональности пользователю нужен только один М 1пс1пс(е. Чтобы облегчить переход на новую версию, можно воспользоваться псевдонимом пространства имен для локализации последствий изменения версии. Всего один файл // 1(Ь.Ь: панезрасе 1(Ь = ге1еаяе1; // ИИИИВИИП Пространства имен пашеврасе Х гпг (, ), к; ) Хпс К; чоьб 11() < (пг Г = Ог ивьпд пашеврасе Х; )+т; к++; :;к++; Х::К+я; // делает доступными имена из Х // локальное Ь // Х~:3 // ошибка: Х::К или глобальное Кт // глобальное К // К из Х чохб Г2() ( Хпс ( = Ор ввгпя Х1:1~ пв(пд Х::3/ пв(пд Х::К; // ошибка: Ь дважды обьявлена в т2() // скрывает глобальное К ( т .~.
// Х::) К++; // Х::К ) пашеврасе А ( гпс х; ) пашеврасе В ( гпс х; ) Тем самым сохраняется важное свойство: локально объявленное имя (введенное либо с помощью обычного, либо с помощью пв(пй-объявления) скрывает не- локальные объявления того же имени, и любая некорректная перегрузка имени обнаруживается в точке объявления. Как видно из вышеприведенного примера, если отдавать приоритет глобальной области действия над пространствами имен, которые в нее включены, то удается в какой-то мере защититься от случайного конфликта имен. С другой стороны, нелокальные имена видимы в контексте, где были объявлены, и трактуются так же, как любые другие нелокальные имена. В частности, ошибки, связанные с пз(пя-директивой, обнаруживаются только в точке использования. Это защищает программиста от сообщений компилятора о потенциальных ошибках.
Например: ЯИИИИИИИ Решение: пространства имен чоьб г () ( пвтпд пашеврасе А; пвтпд пашеврасе в; // правильно: здесь ошибки нег // правильна // правильно // ошибка: А::х или В::х? А::х++; В::х++; х+»; 17.4.%2. Глобальная область действия С появлением пространств имен глобальная область действия с гала лишь еще одним пространством имен. Особенность глобального пространства имен состоит только в том, что его имя не обязательно явно квалифицировать.
Запись;: б означает «б объявлено в глобальном пространстве имен», тогда как х:: б означает «г объявлено в пространстве имен х». Рассмотрим пример: Ьпг а; чотс) г() ( ьпг а = О/ а++) // локальное а ::а+»/ // глобальное а ) Ьпс а; пашеврасе Х ( тпг а; чотб г() ( тпс а = О: а++; // локальное а Х::а++; // х::а ::а++; // х::а или глобальное а? - глобальное а Другими словами, квалификация с помощью унарного оператора:: означает «глобальное» пространство имен, а не то, в котором объявлена переменная. Последнее означало бы, что при погружении произвольного кода в пространство имен его смысл не изменяется.
Но тогда глобальная область действия нс имела бы никакого имени, а это противоречит нашему желанию, чтобы глобальное пространство было лишь частным случаем обычного пространства имен, хотя и с несколько странным именем. Поэтому мы и решили, что оператор:: будет адресовать переменную а, объявленную н глобальной области действия. Если поместить этот код в пространство имен и добавить еще одну переменную а, получим: КИИИИИИ6 Пространства имен пашеврасе Х ( гпс в; Ыс ь; // пвьпд пвшеврасе Х; // сделать доступными все имена из х пвьпд Х::Ь; // объявить локальный синоним для х::Ь Гпс Хт = ::а;// ошибка: в глобальной области 'а' не обьявлена !пс г2 = ::ь;// правильно: будет найден локальный синоним х::ь Отсюда следует, что старый код, в котором явно использовался оператор::, перестанет работать, если погрузить библиотеку в пространство имен. Нужно либо модифицировать код, явно указав новое имя библиотечного пространства имен, либо воспользоваться подходящим глобальным пв)пя-объявлением.
т7.4.5З. Перегрузка Самым запутанным аспектом предложения о пространствах имен был вопрос, следует ли разрешать перегрузку имен, объявленных в разных пространствах. Рассмотрим пример: пвшеврвое А ( уоЫ Е(гпс); // ) пвьпд пашеврасе Л/ пвшеврасе В [ уоЫ г(сьаг)/ // ) пвьпд пвшеврасе в; уоЫ д() ( г('а'); ) // внзнвается в::г(снаг); Пользователь, невнимательно ознакомившийся с пространством имен В, может решить, что будет вызвана )(:: б ( кпс ) . Сложности возникнут и у пользователя, который тщательно изучил программу год назад, но не обратил внимания, что в последней версии в пространство имен В было добавлено объявление г'(сЬаг). Думается, что применять глобальные имена станут значительно реже.
Правила для пространств имен специально были составлены так, чтобы не давать никаких преимуществ ленивым программистам по сравнению с теми, кто сознательно стремится не засорять глобальную область действия. Обратите внимание, что пвшй-директива не объявляет имена в той области действия, где употребляется: ПИИИИИИИ Решение: пространства имен // старый код: чоЫ Г().пе); // из й.'и // чоЫ Г(сваг);// из В.)з // чоЫ д<) г('а'); ) // вызывается г из в.й Для добавления в этот код пространств имен не надо менять ничего, кроме заголовочных файлов.
17.4.5.4. Вложенные пространства имен Одно из очевидных применений пространства имен — помещение в него полного набора объявлений и определений: паыезраса Х ( // все ыои объявления ) Обычно список объявлений также может содержать пространства имен. Поэтому из прагматических соображений вложенные пространства имен допускаются. Это согласуется и с той точкой зрения, что любые конструкции должны вкладываться, если нет веских доводов против такой возможности. Например: чоЫ и() / паыезраса х ( чоЫ д(); // Однако эта проблема возникает лишь тогда, когда вы сопровождаете программу, в которой предложение пэгпд паыеэрасе используется дважды для одной и той же области действий — для вновь разрабатываемых продуктов такая практика не рекомендуется. Кроме того, компилятор вполне может выдать предупреждение о том, что вызов функции разрешен двумя способами за счет объявлений из разных пространств имен, даже если обычные правила перегрузки позволяют выбрать только один вариант.
Я рассматриваю цсйпй-директивы в основном как средство, применимое только в переходный период; авторы новых программ смогут избежать многих теоретических и некоторых практических затруднений, если будут по возможности употреблять квалифицированные имена и цз(пя-объявления. Перегрузка имен, объявленных в разных пространствах, разрешена по двум причинам. С одной стороны, это проще всего, поскольку в таком случае применяются общие правила перегрузки.
С другой стороны, удается обойтись минимальными изменениями кода при включении пространств имен в существующие библиотеки (по крайней мере, ничего лучшего придумать не удалось). Например: ИИИИИИИИВ Пространства имен пашеврасе у ( чоЫ Г(); чоЫ бб(); // ) ) Применимы обычные правила областей действия и квалификации: чоЫ Х::У::ГГ() ( г(); д(); ь(); ) чоЫ Х::д() ( Г(); у::Г(); ) // ошибка: в Х нет Е() чоЫ П() ( Г()! у: пб(); х::т(); х: у::г(); // ошибка: нет глобальной 1() // ошибка: нет глобального у // ошибка: в Х нет г() 17.4.5.5. Пространства имен открыты Пространство имен открыто в том смысле, что его можно составлять из нескольких объявлений, расположенных в разных местах. Например: пашеврасе А ( тпс г(); // сейчас в А есть член г() ); пашеерасе Ь ( (пс д(); // а сейчас в А есть члены б() и д() // мой заголовочный файл: ексегп чоЫ 2(); // моя функция // Это делается для того, чтобы большие фрагменты программы можно было поместить в одно пространство имен.