C++. Пространства имен. Ключевые слова namespace, using

Пространства имен. Ключевые слова namespace, using. Правила создания пространства имен. Глобальное пространство имен


Содержание


Поиск на других ресурсах:

1. Понятие пространства имен. Объявление пространства имен. Ключевое слово namespace. Задание именованного и неименованного пространства имен

До появления пространств имен использовалось одно единое глобальное пространство. Это пространство содержало список общедоступных глобальных переменных и функций, которые можно было использовать в своей программе. Если в программе объявлялись собственные функции, чьи имена совпадали с именами функций глобального пространства имен (например, функции abs(), sqrt() и т.п.), то имена этих локальных функций перекрывали глобальные. Это вызывало неудобства и требовало более внимательного отношения к выбору имен. Также проблема выбора имен возникала при одновременном использовании нескольких сторонних библиотек.

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

namespace Name
{
  // Объявление переменных, констант, типов, функций
  // ...
}

После объявления пространства имен можно обращаться к его элементам. Имя пространства имен можно не указывать, если это имя объявлено в этом же пространстве имен.

За пределами пространства имен обращение выглядит следующим образом

Name::item

де

  • Name – имя пространства имен;
  • item – имя элемента (функции, класса, константы, переменной) который объявлен в пространстве имен.

Пространство имен может быть объявлено без имени по следующему синтаксису

namespace
{
  // составляющие пространства имен
  // ...
}

С точки зрения размещения пространство имен может быть объявлено:

  • в одном модуле;
  • в нескольких модулях (см. примеры ниже).

 

2. Пример объявления и использования пространства имён. Все элементы размещаются в одном модуле

В примере объявляется пространство имен MATH, содержащее следующие объявления:

  • константа Pi;
  • функция Circumference() – возвращает длину окружности на основе заданного радиуса;
  • функция AreaCircle() – возвращает площадь круга на основе заданного радиуса;
  • функция VolumeSphere() – возвращает объем шара на основе заданного радиуса.

В функции main() демонстрируется доступ к некоторым составляющим пространства имен MATH.

 

#include <iostream>
using namespace std;

// Пространство имен MATH
namespace MATH
{
  // Константа в пространстве имен
  const double Pi = 3.1415;

  // Функция, возвращающая длину окружности
  double Circumference(double r)
  {
    return 2 * Pi * r;
  }

  // Функция, возвращающая площадь круга
  double AreaCircle(double r)
  {
    return Pi * r * r;
  }

  // Функция, возвращающая объем шара
  double VolumeSphere(double r)
  {
    return 4.0 / 3 * Pi * r * r * r;
  }
}

void main()
{
  cout << "Pi = " << MATH::Pi << endl;
  cout << "V = " << MATH::VolumeSphere(3) << endl;
}

 

3. Директива using. Доступ к элементам пространства имен

С целью повышения читабельности программы и избегания постоянного указания имени пространства имен при доступе к его элементам используется директива using. Эта директива может применяться в двух случаях:

using namespace Name; // подключение пространства имен
using Name::member;   // подключение отдельного элемента

здесь

  • Name – имя пространства имен;
  • member – имя элемента пространства имен. Элементом могут быть константа, функция, класс и т.д.

В первом случае подключается все пространство имен. После подключения доступ к элементам этого пространства имен можно производить непосредственно без указания фрагмента Name::.

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

 

3.1. Пример доступа ко всем элементам пространства имен

В примере объявляется пространство имен MATH. Затем, в целях демонстрации, это пространство имен подключается с помощью строки

using namespace MATH;

В теле функции main() происходит обращение к составляющим пространства имен MATH (константа Pi, функция VolumeSphere()). Обращение происходит без указания префикса MATH::.

 

#include <iostream>
using namespace std;

// Пространство имен MATH
namespace MATH
{
  // Константа в пространстве имен
  const double Pi = 3.1415;

  // Функция, возвращающая длину окружности
  double Circumference(double r)
  {
    return 2 * Pi * r;
  }

  // Функция, возвращающая площадь круга
  double AreaCircle(double r)
  {
    return Pi * r * r;
  }

  // Функция, возвращающая объем шара
  double VolumeSphere(double r)
  {
    return 4.0 / 3 * Pi * r * r * r;
  }
}

using namespace MATH;

void main()
{
  // Обращение к составляющим пространства имен MATH
  // без указания MATH::
  cout << "Pi = " << Pi << endl;
  cout << "V = " << VolumeSphere(3) << endl;
}

Результат выполнения программы

Pi = 3.1415
V = 113.094

 

3.2. Пример доступа к отдельному элементу в пространстве имен

В примере объявляется пространство имен Operations и показывается доступ к методам Sub2() и Mul2() этого пространства из функции main().

 

#include <iostream>
using namespace std;

// Пространство имен, в котором определены базовые операции над двумя числами
namespace Operations
{
  double Add2(double a, double b)
  {
    return a + b;
  }

  double Sub2(double a, double b)
  {
    return a - b;
  }

  double Mul2(double a, double b)
  {
    return a * b;
  }

  double Div2(double a, double b)
  {
    if (b == 0)
      return 0;
    return a / b;
  }
}

// Реализовать доступ к методам Sub2() и Mul2()
using Operations::Sub2;
using Operations::Mul2;

void main()
{
  double x = 10, y = 8;
  double z = Sub2(x, y); // z = 2
  cout << "z = " << z << endl;

  z = Mul2(x, y);
  cout << "z = " << z << endl;

  // Ошибка, нету доступа
  // z = Add2(x, y); // identifier Add2 is undefined
}

Результат выполнения программы

z = 2
z = 80

В вышеприведенном примере подключение методов осуществляется директивой using с помощью строк

using Operations::Sub2;
using Operations::Mul2;

После этого эти методы могут быть вызваны непосредственно без использования префикса Operations::. Если попытаться вызвать неподключенный метод Add2()

// Ошибка, нету доступа
z = Add2(x, y);

то компилятор выдаст сообщение об ошибке

identifier Add2 is undefined

 

4. Глобальное пространство имен ::

Глобальное пространство имен определяет область видимости самого высокого уровня. В данной области видимости могут быть объявлены глобальные переменные, типы, функции и т.д.

Для доступа к глобальному пространству имен необходимо использовать оператор расширения области видимости ::. Если в глобальном пространстве имен и в некотором локальном пространстве есть совпадающие имена, то для доступа к глобальному имени использование оператора :: обязательно.

Пример.

В примере объявляется:

  • глобальная функция ShowPi(), выводящая значение числа Пи;
  • пространство имен MathItems, содержащее некоторые константы и функцию ShowPi(), имя которой совпадает с именем глобальной функции.

В функции main() демонстрируется доступ к одноименным функциям.

#include <iostream>
using namespace std;

// Глобальная функция, выводящая значение числа Pi
void ShowPi()
{
  cout << "Pi = 3.1415" << endl;
}

// Пространство имен, в котором определены константы и функции
namespace MathItems
{
  const double Pi = 3.1415;
  const double E = 2.71828;

  // Функция, которая отображает значение числа Pi из пространства имен
  void ShowPi()
  {
    cout << "MathItems::Pi = " << Pi << endl;
  }
}

using namespace MathItems;

void main()
{
  // 1. Вызов глобальной функции ShowPi()
  ::ShowPi();

  // 2. Вызов функции ShowPi() из пространства имен MathItems
  MathItems::ShowPi();

  // 3. Ошибка - неоднозначный вызов перегруженной функции
  //ShowPi(); // ambiguous call to overloaded function
}

Результат выполнения программы

Pi = 3.1415
MathItems::Pi = 3.1415

Если в вышеприведенном коде в функции main() попробовать реализовать вызов

// Ошибка - неоднозначный вызов
ShowPi();

то компилятор сгенерирует ошибку

ambiguous call to overloaded function

Это логично, поскольку неизвестно, к какой функции ShowPi() происходит обращение.

 

5. Повторное объявление пространства имен. Пример

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

namespace Name
{
  // Часть 1. Описание составляющих
  // ...
}

namespace Name
{
  // Часть 2. Описание составляющих
  // ...
}

...

namespace Name
{
  // Часть N. Описание составляющих
  // ...
}

Пример.

В примере реализовано пространство имен Operations с несколькими функциями. Это пространство имен разбито на 2 части.

 

#include <iostream>
using namespace std;

// Пространство имен, которое определяет базовые операции над двумя числами
namespace Operations
{
  double Add2(double a, double b)
  {
    return a + b;
  }

  double Sub2(double a, double b)
  {
    return a - b;
  }
}

namespace Operations
{
  double Mul2(double a, double b)
  {
    return a * b;
  }

  double Div2(double a, double b)
  {
    if (b == 0)
      return 0;
    return a / b;
  }
}

void main()
{
  // Вызов функций из разных частей пространства имен Operations
  cout << Operations::Add2(5, 8) << endl;
  cout << Operations::Mul2(5, 8) << endl;
}

 

6. Объявление пространства имен в разных файлах (модулях)

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

Файл 1.

using namespace Name
{
  // Составляющие пространства имен в файле 1
  // ...
}

Файл 2.

using namespace Name
{
  // Составляющие пространства имен в файле 2
  // ...
}

Файл N.

using namespace Name
{
  // Составляющие пространства имен в файле N
  // ...
}

Пример.

Пусть заданы два модуля:

  • модуль Source.cpp, содержащий объявление первой части пространства имен Operations и функцию main();
  • модуль Operations.h, в котором объявляется вторая часть пространства имен Operations.

В этом случае текст обоих пространств имен следующий.

Модуль Source.cpp.

#include <iostream>
#include "Operations.h"
using namespace std;

// Пространство имен, в котором определены базовые операции
// над двумя числами, первая часть пространства имен
namespace Operations
{
  double Add2(double a, double b)
  {
    return a + b;
  }

  double Sub2(double a, double b)
  {
    return a - b;
  }
}

void main()
{
  // Вызов функций из разных частей пространства имен Operations
  // Из текущего модуля
  cout << Operations::Add2(5, 8) << endl;

  // Из модуля Operations.h
  cout << Operations::Mul2(5, 8) << endl;
}

Модуль Operations.h.

#pragma once

// Вторая часть пространства имен Operations
namespace Operations
{
  double Mul2(double a, double b)
  {
    return a * b;
  }

  double Div2(double a, double b)
  {
    if (b == 0)
      return 0;
    return a / b;
  }
}

 

7. Пространство имен std

В классическом C++ базовым пространством имен есть пространство имен std. В этом пространстве имен реализуются средства стандартной библиотеки C++.

В стандартной библиотеке реализовано тысячи функций, которые полезны при разработке любых программ. Примерами таких функций могут служить математические функции abs(), acos(), sin(), cos() и другие. Подробное рассмотрение возможностей стандартной библиотеки C++ не является предметом обсуждения данной темы.

В предыдущих версиях C++ вся стандартная библиотека была в глобальном пространстве имен. Помещение стандартной библиотеки в пространство имен std существенно снижает возможность конфликтов имен.

Для подключения пространства имен std используется строка

using namespace std;

Также часто в программах встречается обращение к элементам пространства имен std с указанием полного имени, например

// корень квадратный
double a = std::sqrt(28);

// модуль числа
int x;
x = std::abs(-18);

 

8. Вложенные пространства имен

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

Пример.

В примере объявляется два пространства имен:

  • OUT – это внешнее пространство имен;
  • IN – это пространство имен, которое вложено в пространство имен OUT.

В функции main() демонстрируется доступ к составляющим пространств имен OUT и IN.

#include <iostream>
using namespace std;

// Вложенные пространства имен
namespace OUT
{
  void MethodOut() { }
  int var_out;

  namespace IN
  {
    int var_in = var_out;

    void MethodIn()
    {
      MethodOut();
      var_out = 3;
      cout << "var_out = " << var_out << endl;
    }
  }
}

void main()
{
  // использование оператора ::
  OUT::IN::var_in = 35;
  OUT::IN::MethodIn();

  // после такой строки
  using namespace OUT;
  IN::var_in = 333;
  cout << "IN::var_in = " << IN::var_in << endl;

  // после такой строки
  using namespace OUT::IN;
  MethodIn(); // можно обращаться к методу непосредственно
}

Результат выполнения программы

var_out = 3
IN::var_in = 333
var_out = 3