Пространства имен
Рано или поздно практически каждый программист сталкивается с проблемой конфликта идентификаторов, особенно если проект разрабатывают несколько программистов либо привлекаются библиотеки классов, поставляемые кем-то другим. Приходится искать какой-то способ избежать употребления глобальных имен, используемых имеющимися библиотеками, другими программистами группы и т. п.
Язык C++ решает эту проблему, позволяя разбить проект на сегменты с различными пространствами имен (namespaces). Заключив свои символы в отдельное пространство имен, вы, по существу, снабжаете имя всякого типа, переменной и т. д. некоторым скрытым префиксом. Например, если определить пространство имен
namespace MYSPACE { int х;
}
то переменная х будет существовать в пространстве имен MYSPACE. Любые другие переменные х, принадлежащие глобальной области действия или другому пространству имен, не будут с ней конфликтовать. Чтобы явно обратиться к переменной, применяется операция разрешения области действия:
MYSPACE::х = 11;
Пространства имен могут быть вложенными:
namespace OUTER { int x;
namespace INNER { int x;
}
Соответственно, чтобы ссылаться на эти переменные, нужно будет написать:
OUTER::х = 11;
OUTER::INNER::x = 22;
Пространства имен можно создавать и не только в глобальной области действия, но и, например, в функциях. Но это редко имеет практический смысл.
Создание пространства имен
К созданию именных пространств можно отнести три момента:
Первоначальное объявление пространства имен — это определение пространства с именем, еще не встречавшимся в программе. Синтаксис его следующий:
namespace новое_имя {тело пространства имен}
Тем самым в программе объявляется пространство имен с именем новое_имя.
Объявленное пространство имен можно расширять, в том же исходном файле или любом другом, применяя ту же конструкцию, но с уже имеющимся именем:
namespace существующее_имя [тело_пространства__имен]
Тела всех пространств с одинаковыми именами, определенных на глобальном уровне, составляют одно именное пространство. Каждый идентификатор в его пределах должен быть уникален.
Синтаксис C++ позволяет определять анонимные пространства имен:
namespase{
int x;
double у;
}
Все анонимные пространства имен в глобальной области действия данного файла составляют единое целое. Говоря проще, объявление глобальных символов как относящихся к анонимному пространству имен эквивалентно их объявлению с модификатором static:
static int x;
static double у;
Доступ к пространству имен
Доступ к элементам конкретного пространства имен может осуществляться тремя способами:
Примеры явной квалификации имен мы уже приводили чуть выше. Имя снабжается префиксом, состоящим из идентификатора (квалификатора) пространства имен со знаком операции разрешения области действия.
Квалифицирующее объявление выглядит следующим образом:
using иденгификатор_пространства_имен::имя;
Элемент указанного пространства имен, идентифицируемый именем, объявляется как принадлежащий локальной области действия. После этого все не квалифицированные ссылки на имя будут рассматриваться как относящиеся к данному элементу. Вот пример:
namespace ALPHA {
int RetJ(int j) {return j;} } namespace BETA {
int RetJ(int j) {return (j * 2);}
}
int main(void) {
using BETA::RetJ; // Квалифицирующее объявление RetJ().
int x = 11;
printf("Calling RetJ(): %d\n"/ RetJ(x));
// Вызывает BETA::RetJ(). return 0;
}
Последняя форма доступа к пространству имен — директива using. Она имеет вид:
using namespace идентификатор пространства_имен;
В результате указанное пространство имен будет приниматься по умолчанию, если ссылка на некоторое имя не может быть разрешена локально (как и в общем случае, локальное объявление скрывает любое объявление в “более внешней” области действия). Вот пример, демонстрирующий различные моменты применения именных пространств:
#include <stdio.h>
namespace ALPHA {
int x = 11;
int у = 111;
int v = 1111;
} namespace BETA {
int x = 22;
int у - 222;
int z = 2222;
}
int main(void) {
using namespace ALPHA; // Конфликта не возникает до
using namespace BETA; // момента действительного
// обращения к элементам х и у.
using ВЕТА::х;
// Локальная квалификация х.
int z = 3333; // Локальная переменная.
printf ("Global х = %d, у = %d, z = %d, v = %d.\n", х, ALPHA::y, ::z, v);
// х квалифицирована
// локально, у - явно.
printf("Local z = %d.\n", z) ;
return 0;
}
Программа печатает:
Global х = 22, у = 111, z = 2222, v = 1111. Local z = 3333.
Здесь показан случай, когда могут возникать конфликты между двумя пространствами имен, используемыми одновременно. В теле главной процедуры указаны два именных пространства ALPHA и BETA, которые оба объявляют глобальные переменные х и у. В данный момент у компилятора не возникает никаких возражений. Ошибка случилась бы, например, если ниже мы обратились к переменной у без какой-либо квалификации. В общем, разберите внимательно этот пример — каким образом компилятор разрешает неоднозначные ссылки на различные символы.
Псевдонимы именных пространств
Можно объявлять псевдонимы для именных пространств, если, например, полная квалификация символа оказывается слишком уж длинной:
namespace BORLAND_INTERNATIONAL
{
// Тело именного пространства...
namespace NESTED_BORLAND_INTERNATIONAL
{
// Тело вложенного именного пространства... }
// Псевдонимы:
namespace BI = BORLAND_INTERNATIONAL;
namespace NBI = BORLAND_INTERNATIONAL::
NESTED_BORLAND_INTERNATIONAL;