- •7. Робота з шаблонними, функціями та класами
- •7.1. Поняття про узагальнені функції
- •7.1.1. Механізм реалізації шаблонної функції з одним узагальненим типом
- •7.1.2. Безпосередньо задане перевизначення узагальненої функції
- •7.1.3. Шаблонна функція з двома узагальненими типами
- •7.1.4. Механізм перевизначення специфікації шаблону функції
- •7.1.5. Використання стандартних параметрів у шаблонних функціях
- •7.2. Поняття про узагальнені класи
- •7.1.6. Обмеження, які застосовуються при використанні узагальнених функцій
- •7.1.7. Приклад створення узагальненої функції abs()
- •7.2.1. Створення класу з одним узагальненим типом даних
- •7.2.2. Створення класу з двома узагальненими типами даних
- •7.2.3. Приклад створення узагальненого класу для організації безпечного масиву
- •7.2.4. Використання в узагальнених класах аргументів, що не є узагальненими типами
- •11.2.1. Передача покажчиком на функцію її адреси іншій функції
- •9.2.3. Класи потоків
- •9.3. Особливості механізмів перевизначення операторів введення-виведення даних
- •9.3.1. Створення перевизначених операторів виведення даних
- •9.5. Організація файлового введення-виведення даних
- •9.5.1. Відкриття та закриття файлу
- •9.5.2. Зчитування та запис текстових файлів
- •9.5.3. Неформатне введення-виведення даних у двійковому режимі
- •9.5.4. Зчитування та записування у файл блоків даних
- •10. Динамічна ідентифікація типів і оператори приведення типу
- •10.1. Динамічна ідентифікація типів
- •10.1.1. Отримання типу об'єкта у процесі виконання програми
- •10.1.2. Приклад rtti-застосування
- •11. Поняття про простори імен та інші ефективні програмні засоби
- •11.1. Особливості організації простору імен
- •11.1.1. Поняття про простори імен
- •11.1.2. Застосування настанови using
- •11.1.3. Неіменовані простори імен
- •11.1.4. Застосування простору імен std
- •11.2. Застосування покажчиків на функції
- •11.2.2. Пошук адреси перевизначеної функції
- •11.3. Поняття про статичні члени-даних класу
- •11.5. Застосування до функцій-членів класу модифікаторів const і mutable
- •11.6. Використання explicit-конструкторів
- •12. Введення в стандартну бібліотеку шаблонів
- •12.1. Огляд стандартної бібліотеки шаблонів
- •12.2. Поняття про контейнерні класи
- •12.3. Механізми роботи з векторами
- •12.3.1. Доступ до елементів вектора за допомогою ітератора
- •12.3.2. Вставлення та видалення елементів з вектора
- •12.3.3. Збереження у векторі об'єктів класу
- •12.3.4. Доцільність використання ітераторів
- •7. Робота з шаблонними, функціями та класами 1
- •7.1. Поняття про узагальнені функції 1
- •10. Динамічна ідентифікація типів і оператори приведення типу 26
- •11. Поняття про простори імен та інші ефективні програмні засоби 31
- •12. Введення в стандартну бібліотеку шаблонів 44
11. Поняття про простори імен та інші ефективні програмні засоби
У цьому розділі буде описано основні поняття про простори імен і такі ефективні програмні засоби, як explicit-конструктори, покажчики на функції, static-члени, const-функції-члени, альтернативний синтаксис ініціалізації членів-даних класу, оператори вказання на члени класу, ключове слово asm, специфікація компонування і функції перетворення.
11.1. Особливості організації простору імен
Простори імен стисло розглянуто у розд. 2 [9]. Там було сказано, що вони дають змогу локалізувати імена ідентифікаторів, щоб уникнути конфліктних ситуацій з ними. У С++-середовищі програмування використовується величезна кількість імен змінних, функцій та імен класів. До введення поняття простору імен всі ці імена конкурували між собою за пам'ять в глобальному просторі імен, що і було причиною виникнення багатьох конфліктів. Наприклад, якби у Вашій програмі було визначено функцію toupper(), то вона могла б (залежно від переліку параметрів) перевизначити стандартну бібліотечну функцію toupper(), оскільки обоє імен мали б зберігатися в глобальному просторі імен. Конфлікти з іменами виникали раніше також під час використання однією програмою декількох бібліотек сторонніх виробників. У цьому випадку ім'я, що є визначеним у одній бібліотеці, конфліктувало з таким самим іменем з іншої бібліотеки. Така ситуація особливо неприйнятна під час використання однойменних класів. Наприклад, якщо у Вашій програмі визначено клас VideoMode, і в бібліотеці, що використовуються Вашою програмою, визначено клас з таким самим іменем, то конфлікту не уникнути.
Для вирішення описаної проблеми було створено ключове слово namespace. Оскільки воно локалізує видимість оголошених у ньому імен, то це означає, що простір імен дає змогу використовувати одне і те саме ім'я в різних контекстах, не викликаючи при цьому конфлікту імен. Можливо, найбільше від нововведення "поталанило" С++-бібліотеці стандартних функцій. До появи ключового слова namespace (яке було, звичайно ж, єдиним) всю С++-бібліотеку було визначено в глобальному просторі імен. З настанням namespace-''ери" С++-бібліотека визначається у власному просторі імен, названий std, який значно знизив ймовірність виникнення конфліктів імен. У своїй програмі програміст повинен створювати власні простори імен, щоб локалізувати видимість тих імен, які, на його думку, можуть стати причиною конфлікту. Це особливо важливо, якщо Ви займаєтеся створенням бібліотек класів або функцій.
11.1.1. Поняття про простори імен
Ключове слово namespace дає змогу розділити глобальний простір імен шляхом створення певної декларативної області. По суті, простір імен визначає область видимості. Загальний формат задавання простору імен є таким:
namespace name {
// Оголошення
}
Все, що визначено у межах настанови namespace, знаходиться в області видимості цього простору імен.
У наведеному нижче коді програми показано приклад використання namespace-настанови. Вона локалізує імена, що використовуються для виконання розрахунку у зворотному порядку. У створеному тут просторі імен визначається клас counter, який реалізує лічильник, і змінні upperbound і lowerbound, що містять значення верхньої і нижньої меж, які використовуються для всіх лічильників.
// Приклад використання namespace-настанови
namespace CounterNameSpace
{
int upperbound;
int lowerbound;
class counter {
int count;
public:
counter(int n) {
if(n <= upperbound) count = n;
else count = upperbound;
}
void Reset(int n) {
if(n <= upperbound) count = n;
}
int Run() {
if(count > lowerbound) return count--;
else return lowerbound;
}
};
}
У цьому коді програми змінні upperbound і lowerbound, а також клас counter є частиною області видимості, визначеної простором імен CounterNameSpace.
У будь-якому просторі імен до ідентифікаторів, які у ньому оголошені, можна звертатися безпосередньо, тобто без вказання цього простору імен. Наприклад, у функції Run(), яка знаходиться у просторі імен CounterNameSpace, можна безпосередньо звертатися до змінної lowerbound:
if(count > lowerbound) return count;
Але, оскільки настанова namespace визначає область видимості, то під час звернення до об'єктів, оголошених у просторі імен, ззовні цього простору необхідно використовувати оператор дозволу області видимості. Наприклад, щоб присвоїти значення 10-ій змінній upperbound з коду програми, який є зовнішнім стосовно простору імен CounterNameSpace, потрібно використовувати таку настанову:
CounterNameSpace::upperbound = 10;
Щоб оголосити об'єкт типу counter поза простором імен CounterNameSpace, пот-
рібно використати настанову, подібну до такої:
CounterNameSpace::counter obj;
У загальному випадку, щоб отримати доступ до деякого члена простору імен ззовні цього простору, необхідно, щоби імені цього члена передувало ім'я простору і розділити ці імена оператором дозволу області видимості.
Розглянемо невелику програму, у якій продемонстровано механізм використання простору імен CounterNameSpace.
Демонстрація механізму використання простору імен CounterNameSpace для розрахунку у зворотному порядку
#include <vcl>
#include <iostream> // Для потокового введення-виведення
#include <conio> // Для консольного режиму роботи
using namespace std; // Використання стандартного простору імен
// Приклад використання namespace-настанови
namespace CounterNameSpace
{
int upperbound;
int lowerbound;
class counter {
int count;
public:
counter(int n) {
if(n <= upperbound) count = n;
else count = upperbound;
}
void Reset(int n) {
if(n <= upperbound) count = n;
}
int Run() {
if(count > lowerbound) return count--;
else return lowerbound;
}
};
}
int main()
{
CounterNameSpace::upperbound = 100;
CounterNameSpace::lowerbound = 0;
CounterNameSpace::counter ObjA(10);
CounterNameSpace::counter ObjB(20);
int c;
cout << "Розрахунок у зворотному порядку для об'єкта ObjA" << endl;
do {
c = ObjA.Run();
cout << c << " ";
} while(c > CounterNameSpace::lowerbound);
cout << endl;
cout << "Розрахунок у зворотному порядку для об'єкта ObjB" << endl;
do {
c = ObjB.Run();
cout << c << " ";
} while(c > CounterNameSpace::lowerbound);
cout << endl;
ObjB.Reset(100);
CounterNameSpace::lowerbound = 80;
cout << "Розрахунок у зворотному порядку для об'єкта ObjB" << endl;
do {
c = ObjB.Run();
cout << c << " ";
} while(c > CounterNameSpace::lowerbound);
cout << endl;
getch(); return 0;
}
Зверніть увагу на те, що при створенні об'єкта класу counter і при зверненні до змінних upperbound і lowerbound використовують ім'я простору імен CounterNameSpace. Але після створення об'єкта типу counter вже немає потреби у повній кваліфікації його самого або його членів. Оскільки простір імен однозначно визначено, то функцію Run() об'єкта ObjA можна викликати безпосередньо, тобто без вказання (як префікс) простору імен (ObjA.Run()).
Програма може містити декілька оголошень просторів імен з однаковими
іменами. Це означає, що простір імен можна поділити на декілька файлів або на
декілька частин у рамках одного файлу. Зробити це можна так:
namespace NS {
int c;
}
//....
namespace NS {
int d;
}
У цьому записі простір імен NS розділено на дві частини. Проте вміст кожної
частини належить до одного і того ж простору імен NS
Будь-який простір імен повинен бути оголошений поза всіма іншими областями видимості. Це означає, що не можна оголошувати простори імен, які локалізовані, наприклад, у межах функції. При цьому один простір імен може бути вкладено в інший.
