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