Якщо ви часто використовуєте Стандартну бібліотеку C++, то постійне додання std::
до використовуваних об’єктів може бути набридливим, чи не так? Мова C++ надає альтернативу у вигляді using-стейтментів.
Використання “using-оголошення”
Однією з альтернатив є використання “using-оголошення“. Ось програма «Hello, world!» з “using-оголошенням” в рядку №5:
1 2 3 4 5 6 7 8 |
#include <iostream> int main() { using std::cout; // "using-оголошення" повідомляє компілятору, що cout потрібно обробляти як std::cout cout << "Hello, world!"; // і ніякого префіксу std:: тут не потрібно! return 0; } |
Рядок using std::cout;
повідомляє компілятору, що ми будемо використовувати об’єкт cout з простору імен std. І кожен раз, коли компілятор стикатиметься з cout
, він розумітиме, що це std::cout
.
Звичайно, в цьому випадку ми не заощадили багато зусиль, але в програмі, де об’єкти з простору імен std використовуються сотні, якщо не тисячі разів, “using-оголошення” непогано так економлять час, зусилля та покращують читабельність коду. Також для кожного об’єкту потрібно використовувати окреме “using-оголошення” (наприклад, окреме для std::cout
, окреме для std::cin
і окреме для std::endl
).
Хоча цей спосіб є менш привабливим, ніж використання префікса std::
, він все ж є абсолютно безпечним і прийнятним.
Використання “using-директиви”
Іншою альтернативою є використання “using-директиви“. Ось програма «Hello, world!» з “using-директивою” в рядку №5:
1 2 3 4 5 6 7 8 |
#include <iostream> int main() { using namespace std; // "using-директива" повідомляє компілятору, що ми використовуємо всі об'єкти з простору імен std! cout << "Hello, world!"; // так що ніякого префіксу std:: тут не потрібно! return 0; } |
Багато програмістів сперечаються щодо використання “using-директиви”. Оскільки з її допомогою ми підключаємо ВСІ імена з простору імен std, то ймовірність виникнення конфліктів імен значно зростає (але все ж ця ймовірність в глобальному масштабі залишається незначною). using namespace std;
повідомляє компілятору, що ми хочемо використовувати все, що знаходиться в просторі імен std, тому якщо компілятор знайде ім’я, яке не зможе розпізнати, він перевірятиме його наявність в просторі імен std.
Порада: Намагайтеся уникати використання “using-директиви” (наскільки це можливо).
Приклад конфлікту з “using-директивою”
Розглянемо приклад, де використання “using-директиви” створює невизначеність:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
#include <iostream> int cout() // оголошуємо нашу власну функцію cout() { return 4; } int main() { using namespace std; // робимо std::cout доступним по "cout" cout << "Hello, world!"; // який cout компілятор буде тут використовувати? Той, що з простору імен std чи той, що ми визначили вище? return 0; } |
Тут компілятор не зможе зрозуміти, чи використовувати std::cout чи функцію cout(), яку ми визначили самі. В результаті отримаємо помилку неоднозначності. Хоча це і банальний приклад, але якби ми додали префікс std::
до cout:
1 |
std::cout << "Hello, world!"; // повідомляємо компілятору, що хочемо використовувати std::cout |
Чи використали би “using-оголошення” замість “using-директиви”:
1 2 |
using std::cout; // повідомляємо компілятору, що cout означає std::cout cout << "Hello, world!"; // так що тут потрібно використовувати std::cout |
Тоді наша програма була б без помилок.
Більшість програмістів уникають використання “using-директиви” саме з цієї причини. Інші вважають це прийнятним доти, доки “using-директива” використовується тільки в межах окремих функцій (що значно зменшує масштаби виникнення конфліктів імен).
Область видимості “using-оголошення” і “using-директиви”
Якщо “using-оголошення” чи “using-директива” використовуються в блоці, то вони застосовуються тільки всередині цього блоку (за звичайними правилами локальної області видимості). Це добре, оскільки зменшує масштаби виникнення конфліктів імен до окремих блоків. Однак багато початківців пишуть “using-директиву” в глобальній області видимості (поза функцією main() або взагалі поза будь-яких функцій). Цим вони витягують всі імена з простору імен std безпосередньо в глобальну область видимості, значно збільшуючи ймовірність виникнення конфліктів імен. А це вже не є добре.
Правило: Ніколи не використовуйте using-стейтменти поза тілом функцій.
Відміна/заміна using-стейтментів
Як тільки один using-стейтмент був оголошений, його неможливо скасувати або замінити іншим using-стейтментом в межах області видимості, в якій він був оголошений, наприклад:
1 2 3 4 5 6 7 8 9 |
int main() { using namespace Boo; // Відмінити «використання простору імен Boo» тут неможливо! // Також немає ніякого способу замінити «using namespace Boo» на інший using-стейтмент return 0; } // дія using namespace Boo закінчується тут |
Найкраще, що ви можете зробити — це навмисно обмежити область застосування using-стейтментів з самого початку, використовуючи правила локальної області видимості:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
int main() { { using namespace Boo; // тут все відноситься до простору імен Boo:: } // дія using namespace Boo закінчується тут { using namespace Foo; // тут все відноситься до простору імен Foo:: } // дія using namespace Foo закінчується тут return 0; } |
Звичайно, весь цей головний біль можна було б уникнути, просто використавши оператор дозволу області видимості (::
).