Б. Страуструп - Дизайн и Эволюция C++. 2006 (1160775), страница 102
Текст из файла (страница 102)
Ведь находятся же в одном пространстве имен современныс библиотеки и приложения! Необходимо распределить определение пространства имен по нескольким заголовочным и исходным файлам. Такая открытость заодно упрошаст и переход к использованию пространств имен. Например, текст 1611ИИИП Классы и пространства имен ()тпс1цс)е <вссфо.)з> ексегп (пс д()/ // моя функция // можно переписать, не меняя порядок объявлений: // мой заголовочный файл: паиеврасе Мтпе чоф<) Г(); // моя функция // ()фпс1цс)е <вес)фо.)з> патеврасе Мфпе ( тпс о(); // моя функция // ) 17.5. Классы и пространства имен Не думаю, что однажды поступившее предложение сделать пространство имен разновидностью класса — хорошая мысль, поскольку многие возможности, предоставляемые классами, лишь поддерживают концепцию определенного пользователем типа данных.
Например, средства создания объектов и манипулирования ими имеют мало общего с областями действия. Противоположная точка зрения — класс является частным случаем пространства имен — почти не вызывает сомнений. Класс действительно есть пространство имен в том смысле, что все операции, поддерживаемые для пространств имен, применимы с той же семантикой и к классам, если только конкретная операция для классов явно не запрещена. Это позволяет достичь простоты и единства языка, одновременно сводя к минимуму затраты на реализацию. Такой взгляд подтверждается легкостью, с которой пространства имен вошли в Сч-ч-, и тем, как естественно с их помощью решаются давно стоящие и, казалось бы, не связанные между собой проблемы. 17.5.1.
Производные классы Рассмотрим известную проблему, связанную с тем, что член производного класса скрывает член базового класса с тем же именем: Многие, в том числе и я, предпочитают использовать несколько маленьких пространств имен, вместо того чтобы помещать крупные фрагменты кода в одно пространство. Подобный стиль можно навязать, если требовать, чтобы все члены входили в единственное объявление пространства имен точно так же, как в одном объявлении были бы объявлены все члены класса. Но я не видел смысла приносить многие мелкие удобства, которые сопутствуют открытым пространствам имен, в жертву ограничительной системе только для того, чтобы угодить преобладающим на данный момент вкусам.
ИИИИИИИВ Пространства имен с1авв в ( риЫтс: г (сваг); с1авв Р: рпЫ1с В ( рыЫтс: 1(1пг); ): // скрывает г(спаг) чо1б Г(Ра с)) ( с.б('с')/ // вызывается Р:гв(1пс) с1авв в ( рпЫтс: 1(спаг); с1авв Р : рпЫ1с В ( рыЫ1с: 1(1пс); // ввести В::2 в Р, разрешив перегрузку ыв1пд В::1; ); чоИ б(Ра б) ( д.1('с')/ // вызывается Р::г(спаг) ) Как обычно, имена нз двух базовых классов, которым множественно наследует производный класс, могут приводить к неоднозначности (независимо от того, что обозначают имена): всгпсс А ( чогд 1(1пМ; ); вггысс В ( чо1б г(допЬ1е); ); вггысс С : А, В ( чо1с) с() ( 2(1)) // ошибка: А::г(1пг) или В::г(бопЫе) г (1.
0); // ошибка: А:: б (1пс] или В:: г (допЫе) Разумеется, появление пространств имен не изменяет семантики таких примеров. Но возможна новая интерпретация: поскольку Р— зто класс, его область действия представляет собой пространство имен.
Пространство имен Р вложено в В, поэтому Р:: б (1пг ) скрывает В:: б (сЬаг) . Следовательно, вызывается и:: й (йпС ) . Если такое разрешение имени вас не устраивает, можете воспользоваться пз1пя-объявлением, чтобы включить й ( ) нз В в область действия: 11ИИИВИПН Последствия для классов Но теперь для разрешения таких неоднозначностей мы можем добавить пару пв(пя-объявлений, чтобы ввести А:: б и в:: й в область действия сл впглсп С : А, В ( пв1пд А:: й; пв1пд В::г> чо10 д() ( Г(1); // А::1(1) 1(1.0); // В::1(1.0) ) ): Явный механизм такого рода неоднократно предлагался на протяжении нескольких лет. Я помню, как мы обсуждали эту тему с Джонатаном Шопиро во время работы над версией 2.0, но отказались от ее реализации, поскольку механизм казался слишком специализированным и узким.
С другой стороны, пв)пйобъявления — общий механизм, который по счастливой случайности решил и зту проблему. 17.5.2, Использование базовых классов Во избежание путаницы пв)пя-объявление, являющееся членом класса, должно именовать член базового (непосредственного или косвенного) класса. Чтобы не было проблем с правилом доминирования (см. раздел 12.3.1),использование пз(пя-директив в качестве членов классов запрещено. ()япя-объявление, именующее член базового класса, играет важную роль для повышения единообразия доступа: Это более общий и понятный способ добиться того же, что раньше делалось с помощью объявлений доступа (см. раздел 2.10): с1авв Р: рг1чапе В ( рпЫ1с: В::1; // старый способ: объявление доступа )) вггпсл Р : рпЫ1с й ( пв1пд пашеврасе й; ивтпд ::1; ) с1авв В ( рпЫ1с: Г(спал); ).
с1авв Р : ргсчаге В рпЫ1с: пв1пд В::Г~ ); // ошибка: пвтпд-директива в качестве члена // ошибка: ::1 - не член базового класса БИИИИИИ6! Пространства имен Пз1пя-объявление делает такое специализированное объявление доступа излишним. Поэтому объявления доступа провозглашены устаревшими, следовательно, в будущем, когда пользователи перепишут старые тексты, объявления доступа будут исключены из языка. 17.5З. Иосяючение глобальных патических объявлений Часто бывает полезно поместить набор объявлений в пространство имен, просто чтобы не смешивать их с объявлениями в заголовочных файлах или с глобальными объявлениями в других единицах трансляции.
Например: [)(пс1иое <оеаоет.о> папеэрасе и1пе (пг а; ноЫ г() ( /* ... */ ) (пс о[) ( /* ... */ ) ) ЭЫс1исе <оеаоег. Д> патеараса ( 1пс а; аоЫ Г() ( /* ... */ ) 1пс о() ( /* ... */ ) ) Если не считать перегрузки имен из заголовочного файла, это можно эквивалентно переписать следующим образом: [)(пс1иое <оеа<[ег.Ь> эсастс Ыс а; эсас1с чоЫ г[) ( /* ... */ ) асас(с Ыс р[) ( /" ...
*/ ) Обычно такая перегрузка нежелательна, но все-таки ее можно обеспечитгс папеарасе ( [) (пс1иое <пеас[ег.'и> (пс а; чоЫ г() ( /* ... "/ ) (пс д[) ( /" ... */ ) ) Таким образом, пространства имен позволяют объявить устаревшим использование ключевого слова э гас то для управления видимостью глобальных имен. Это оставляет за эгагтс единственную семантику: статически распределенная, нсдублируюшаяся переменная. Во многих случаях неважно, как именно называется пространство имен, лишь бы его имя не конфликтовало с именами других пространств.
Чтобы справиться с этой проблемой более корректно, разрешим использовать безымянное пространство имен: 1!ИИИИИИИ Совместимость с С Безымянное пространство имен ничем не отличается от любого другого, только не нужно писать его имя. По сути дела, папеврасе ( /' ... */ семантически эквивалентно патеарасе ип1цие паве ( /* ... */ ) из1пд павеерасе ип1оие паве; Все безымянные пространства имен в одной и тов же области действия имеют одно и то же уникальное имя.
В частности, все глобальные безымянные пространства имен в одной единице трансляции являются частями одного и того же пространства имен и отличаются от глобальных безымянных пространств имен в других единицах. 17.б. Совместимость с С Функцию с С-компоновкой можно поместить в пространство имен: пажеврасе Х ( ехеегп "С" чоьд Г(тпе); чо1а д(ьпс); ) Это позволяет использовать функции с С-компоновкой точно так же, как любыс другие члены пространства имен, Например: чо1а 'и () х::1(>; х::ч(>; ) папезрасе Х ( ехсегп "С" чо1С 1(1пс); > павеарасе У ( ехсегп "С" чо1с> 1(1пс); ) Однако в одной программе це может быть двух разных функций с С-компоновкой, имеющих одно и то же имя, даже если они находятся в разных пространствах имен.
Небезопасные правила компоновки, принятые в С, затрудняют нахождение такой ошибки. Можно было бы вообще запретить помещение в пространство имен функций с С-компоновкой. Но тогда люди, которым нужен интерфейс с С, совсем не стали бы пользоваться пространствами имен, а предпочли бы засорять глобальное пространство.
Так что зто не выход. Другая альтернатива — гарантировать, что две функции с одним и те же именем, но в разных пространствах имен, на самом деле оказываются различными, лаже если имеют С-компоновку. Например: ИИИВИИИ11 Пространства имен патеарасе Х ( ехсагп "йс)а" то(о С(тпс); ) патеврасе У ( ехсегп "йс)а" тоЫ т((пс); ) зто естественный способ обратиться из С++-программы к функциям в разных пакетах языка Ада.
Проблема состоит в том, как вызвать такую функцию из программы на С. Поскольку в языке С нет механизма разрешения неоднозначности на основе пространств имен, пришлось бы полагаться на соглашение об именовании (почти наверняка зависящее от реализации). Например, программа могла бы вызывать функции )( й и У й. Такое решение было признано неприемлемым, позтому приходится смириться с небезопасными правилами С. Язык С засоряет пространство имен компоновщика, но не глобальные пространства имен единицы трансляции С+и. Обратите внимание, что это проблема С, а не пространств имен С++. Связывание с программой на языке, где есть механизм, аналогичный пространствам имен в С++, реализуется простым и безопасным способом.