У цьому уроці ми розглянемо, що таке конфлікт імен в C++ і як його вирішувати з допомогою просторів імен і оператора дозволу області видимості.
Конфлікт імен в C++
Припустимо, що вам потрібно з’їздити до далеких родичів в інше місто. У вас є тільки їх адреса: м. Пуцьків, вул. Вербова, 13. Потрапивши в місто Пуцьків, ви відкриваєте Google.Карти і бачите, що є дві вулиці з назвою “Вербова”, ще й в протилежних кінцях міста! Яка з них потрібна вам? Якщо у вас немає ніякої додаткової інформації (наприклад, ви знаєте, що їх будинок знаходиться біля аптеки або школи), вам доведеться зателефонувати їм і запитати. Щоб подібної плутанини не виникало, всі назви вулиць в місті повинні бути унікальними.
Аналогічно і в C++, всі ідентифікатори (імена змінних/функцій/класів і т.д.) повинні бути унікальними. Якщо у вашій програмі знаходяться два однакових ідентифікатора, то будьте певні, що ваша програма не скомпілюється: ви отримаєте помилку конфлікту імен.
Приклад конфлікту імен:
a.cpp:
1 2 3 4 5 6 |
#include <iostream> void doSomething(int x) { std::cout << x; } |
b.cpp:
1 2 3 4 5 6 |
#include <iostream> void doSomething(int x) { std::cout << x * 2; } |
main.cpp:
1 2 3 4 5 6 7 8 |
void doSomething(int x); // попереднє оголошення функції doSomething() int main() { doSomething(5); return 0; } |
Якщо взяти файли a.cpp, b.cpp і main.cpp окремо, то вони скомпілюються. Однак, якщо a.cpp і b.cpp розмістити в одному проекті — відбудеться конфлікт імен, так як визначення функції doSomething() знаходиться відразу в обох файлах.
Більшість конфліктів імен відбуваються в двох випадках:
Файли, додані в один проект, мають функцію (чи глобальну змінну) з однаковими іменами (помилка на етапі лінкінгу).
Файл .cpp підключає заголовок, в якому ідентифікатор конфліктує з ідентифікатором з файлу .cpp (помилка на етапі компіляції).
Як тільки програми стають більшими, в них починає використовуватися більше ідентифікаторів. Отже, ймовірність виникнення конфлікту імен значно зростає. Гарна новина полягає в тому, що C++ надає достатньо механізмів для запобігання виникненню конфліктів імен (наприклад, локальна область видимості або простір імен).
Простір імен
У перших версіях C++ всі ідентифікатори зі Стандартної бібліотеки C++ (такі як cin/cout і т.д.) можна було використовувати напряму. Проте, це означало, що будь-який ідентифікатор зі Стандартної бібліотеки С++ потенційно міг конфліктувати з ім’ям, яке ви вибрали для ваших власних ідентифікаторів. Код, який працював, міг раптово отримати конфлікт імен при підключенні нового заголовку зі Стандартної бібліотеки С++. Або, що ще гірше, код, написаний за стандартами однієї версії С++, міг вже не працювати в новій версії С++. Щоб вирішити цю проблему, весь функціонал Стандартної бібліотеки С++ перенесли в спеціальну область — простір імен (англ. “namespace“).
Так як місто гарантує, що всі вулиці в його межах мають унікальні імена, так і простір імен гарантує, що всі його ідентифікатори — унікальні.
Таким чином, std::cout
складається з двох частин: ідентифікатор cout
і простір імен std
. Весь функціонал Стандартної бібліотеки C++ визначено всередині простору імен std
(скорочено від англ. «standard»).
Ми ще поговоримо про простори імен в наступних уроках, а також розповімо, як створити свій власний namespace. Зараз, найголовніше, що вам потрібно запам’ятати, — щоразу, коли ви використовуєте ідентифікатори зі Стандартної бібліотеки С++ (наприклад, cout
), ви повинні повідомляти компілятору, що цей ідентифікатор знаходиться всередині простору імен std
.
Правило: При використанні ідентифікаторів з простору імен, вказуйте простір імен, який ви використовуєте.
Оператор дозволу області видимості ::
Найпростіший спосіб повідомити компілятору, що певний ідентифікатор знаходиться в певному просторі імен — використати оператор дозволу області видимості ::
. Наприклад:
1 |
std::cout << "Hello, world!"; |
Тут ми повідомляємо компілятору, що хочемо використовувати об’єкт cout
з простору імен std
.