Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
C++ для начинающих (Стенли Липпман) 3-е хххх.pdf
Скачиваний:
84
Добавлен:
30.05.2015
Размер:
5.92 Mб
Скачать

С++ для начинающих

413

// определения функций сортировки такие же, как и раньше

Во многих программах на С++ используются объявления с ключевым словом static. Предполагается, что они должны быть заменены безымянными пространствами имен по мере того, как все большее число компиляторов начнет поддерживать это понятие.

Упражнение 8.11

Зачем нужно определять собственное пространство имен в программе? Упражнение 8.12

Имеется следующее объявление operator*(), члена вложенного пространства имен

namespace cplusplus_primer { namespace MatrixLib {

class matrix { /*...*/ };

matrix operator* ( const matrix &, const matrix & ); // ...

}

cplusplus_primer::MatrixLib:

}

Как определить эту функцию в глобальной области видимости? Напишите только прототип.

Упражнение 8.13 Объясните, зачем нужны безымянные пространства имен.

8.6. Использование членов пространства имен А

Использование квалифицированных имен при каждом обращении к членам пространств может стать обременительным, особенно если имена пространств достаточно длинны. Если бы удалось сделать их короче, то такие имена проще было бы читать и набивать. Однако употребление коротких имен увеличивает риск их совпадения с другими, поэтому желательно, чтобы в библиотеках применялись пространства с длинными именами.

К счастью, существуют механизмы, облегчающие использование членов пространств имен в программах. Псевдонимы пространства имен, using-объявления и using-

директивы помогают преодолеть неудобства работы с очень длинными именами.

8.6.1. Псевдонимы пространства имен

Псевдоним пространства имен используется для задания короткого синонима имени

namespace International_Business_Machines

пространства. Например, длинное имя

{ /* ... */ }

может быть ассоциировано с более коротким синонимом:

С++ для начинающих

414

namespace IBM = International_Business_Machines;

Объявление псевдонима начинается ключевым словом namespace, за которым следует короткий псевдоним, а за ним знак равенства и исходное полное имя пространства. Если полное имя не соответствует никакому известному пространству, это ошибка.

Псевдоним может относиться и к вложенному пространству имен. Вспомним слишком

#include "primer.h"

// трудно читать!

void func( cplusplus_primer::MatrixLib::matrix &m )

{

// ...

cplusplLis_primer::MatrixLib::inverse( m ); return m;

длинное определение функции func() выше:

}

Разрешается задать псевдоним для обозначения вложенного cplusplLis_primer::MatrixLib, сделав определение функции более удобным для

#include "primer.h"

// более короткий псевдоним

namespace mlib = cplusplus_primer::MatrixLib;

// читать проще!

void func( mlib::matrix &m )

{

// ...

mlib::inverse( m ); return m;

восприятия:

}

Одно пространство имен может иметь несколько взаимозаменяемых псевдонимов. Например, если псевдоним Lib ссылается на cplusplus_primer, то определение

// псевдоним alias относится к пространству имен cplusplus_primer namespace alias = Lib;

void func( cplusplus_primer::matrix &m ) { // ...

alias::inverse( m ); return m;

функции func() может выглядеть и так:

}

С++ для начинающих

415

8.6.2. Using-объявления

Имеется механизм, позволяющий обращаться к членам пространства имен, используя их имена без квалификатора, т.е. без префикса namespace_name::. Для этого применяются using-объявления.

Using-объявление начинается ключевым словом using, за которым следует

namespace cplusplus_primer { namespace MatrixLib {

class matrix { /* ... */ }; // ...

}

}

// using-объявление для члена matrix

квалифицированное имя члена пространства. Например: using cplusplus_primer::MatrixLib::matrix;

Using-объявление вводит имя в ту область видимости, в которой оно использовано. Так, предыдущее using-объявление делает имя matrix глобально видимым.

После того как это объявление встретилось в программе, использование имени matrix в

глобальной области видимости или во вложенных в нее областях относится к этому члену пространства имен. Пусть далее идет следующее объявление:

void func( matrix &m );

Оно вводит функцию func() с параметром типа cplusplus_primer:: MatrixLib::matrix.

Using-объявление ведет себя подобно любому другому объявлению: оно имеет область видимости, и имя, введенное им, можно употреблять начиная с места объявления и до конца области видимости. Using-объявление может использоваться в глобальной области видимости, равно как и в области видимости любого пространства имен. Оно употребляется и в локальной области. Имя, вводимое using-объявлением, как и любым другим, имеет следующие характеристики:

оно должно быть уникальным в своей области видимости;

оно скрывает одноименную сущность во внешней области;

оно скрывается объявлением одноименной сущности во вложенной области.

Например:

С++ для начинающих

416

namespace blip {

int bi = 16, bj = 15, bk = 23; // прочие объявления

}

int bj = 0; void manip() {

using blip::bi; //

bi в функции manip() ссылается на blip::bi

++bi;

//

blip::bi == 17

using blip::bj; //

скрывает глобальную bj

++bj;

//

bj в функции manip()ссылается на blip::bj

//

blip::bj == 16

int bk;

//

объявление локальной bk

using blip::bk; // ошибка: повторное определение bk в manip()

}

int wrongInit = bk; // ошибка: bk невидима

// надо использовать blip::bk

Using-объявления в функции manip() позволяют ссылаться на членов пространства blib с помощью неквалифицированных имен. Такие объявления не видны вне manip(), и неквалифицированные имена могут применяться только внутри этой функции. Вне ее необходимо употреблять квалифицированные имена.

Using-объявление упрощает использование членов пространства имен. Оно вводит только одно имя. Using-объявление может находиться в определенной области видимости, и, значит, мы способны точно указать, в каком месте программы те или иные члены разрешается употреблять без дополнительной квалификации.

В следующем подразделе мы расскажем, как ввести в определенную область видимости все члены некоторого пространства имен.

8.6.3. Using-директивы

Пространства имен появились в стандартном С++. Предыдущие версии С++ их не поддерживали, и, следовательно, поставляемые библиотеки не помещали глобальные объявления в пространства имен. Множество программ на С++ было написано еще до того, как компиляторы стали поддерживать такую опцию. Заключая содержимое библиотеки в пространство имен, мы можем испортить старое приложение, использующее ее предыдущие версии: все имена из этой библиотеки становятся квалифицированными, т.е. должны включать имя пространства вместе с оператором разрешения области видимости. Те приложения, в которых эти имена употребляются в неквалифицированной форме, перестают компилироваться.

Сделать видимыми имена из библиотеки, используемой в нашей программе, можно с помощью using-объявления. Предположим, что файл primer.h содержит интерфейс новой версии библиотеки, в котором глобальные объявления помещены в пространство имен cplusplus_primer. Нужно заставить нашу программу работать с новой библиотекой. Два using-объявления сделают видимыми имена класса matrix и функции inverse() из пространства cplusplus_primer:

С++ для начинающих

417

#include "primer.h"

using cplusplus_primer::matrix; using cplusplus_primer::inverse;

//using-объявления позволяют использовать

//имена matrix и inverse без спецификации void func( matrix &m ) {

//...

inverse( m ); return m;

}

Но если библиотека достаточно велика и приложение часто использует имена из нее, то для подгонки имеющегося кода к новой библиотеке может потребоваться много using- объявлений. Добавлять их все только для того, чтобы старый код скомпилировался и заработал, утомительно и чревато ошибками. Решить эту проблему помогают using- директивы, облегчающие переход на новую версию библиотеки, где впервые стали применяться пространства имен.

Using-директива начинается ключевым словом using, за которым следует ключевое слово namespace, а затем имя некоторого пространства имен. Это имя должно ссылаться на определенное ранее пространство, иначе компилятор выдаст ошибку. Using-директива

позволяет сделать все имена из этого пространства видимыми в неквалифицированной форме.

#include "pnmer.h"

//using-директива: все члены cplusplus_primer

//становятся видимыми

using namespace cplusplus_primer;

// имена matrix и inverse можно использовать без спецификации void func( matrix &m ) {

// ...

inverse( m ); return m;

Например, предыдущий фрагмент кода может быть переписан так:

}

Using-директива делает имена членов пространства имен видимыми за его пределами, в том месте, где она использована. Например, приведенная using-директива создает иллюзию того, что все члены cplusplus_primer объявлены в глобальной области видимости перед определением func(). При этом члены пространства имен не получают

namespace A { int i, j;

локальных псевдонимов, а как бы перемещаются в новую область видимости. Код

}

выглядит как

С++ для начинающих

418

int i, J;

для фрагмента программы, содержащего в области видимости следующую using- директиву:

using namespace A;

Рассмотрим пример, позволяющий подчеркнуть разницу между using-объявлением (которое сохраняет пространство имен, но создает ассоциированные с его членами локальные синонимы) и using-директивой (которая полностью удаляет границы

namespace blip {

int bi = 16, bj = 15, bk = 23; // прочие объявления

}

int bj = 0;

void manip() {

using namespace blip; // using-директива -

 

 

 

// коллизия имен ::bj and blip::bj

 

 

 

// обнаруживается только при

 

 

++bi;

// использовании bj

 

 

// blip::bi == 17

 

 

++bj;

// ошибка: неоднозначность

 

 

++::bj;

// глобальная bj или blip::bj?

 

 

// правильно: глобальная bj == 1

 

 

++blip::bj;

// правильно: blip::bj == 16

 

 

int bk = 97;

// локальная bk скрывает blip::bk

 

 

++bk;

// локальная bk == 98

пространства имен).

 

 

 

}

 

 

 

 

 

 

 

Во-первых, using-директивы имеют область видимости. Такая директива в функции manip() относится только к блоку этой функции. Для manip() члены пространства имен blip выглядят так, как будто они объявлены в глобальной области видимости, а следовательно, можно использовать их неквалифицированные имена. Вне этой функции необходимо употреблять квалифицированные.

Во-вторых, ошибки неоднозначности, вызванные применением using-директивы, обнаруживают себя при реальном обращении к такому имени, а не при встрече в тексте самой этой директивы. Например, переменная bj, член пространства blib, выглядит для manip() как объявленная в глобальной области видимости, вне blip. Однако в глобальной области уже есть такая переменная. Возникает неоднозначность имени bj в функции manip(): оно относится и к глобальной переменной, и к члену пространства blip. Ошибка проявляется только при упоминании bj в функции manip(). Если бы это имя вообще не использовалось в manip(), коллизия не проявилась бы.

В-третьих, using-директива не затрагивает употребление квалифицированных имен. Когда в manip() упоминается ::bj, имеется в виду переменная из глобальной области видимости, а blip::bj обозначает переменную из пространства имен blip.

И наконец члены пространства blip выглядят для функции manip() так, как будто они объявлены в глобальной области видимости. Это означает, что локальные объявления внутри manip() могут скрывать имена членов пространства blip. Локальная