- •7. Робота з шаблонними, функціями та класами
- •7.1. Поняття про узагальнені функції
- •7.1.1. Механізм реалізації шаблонної функції з одним узагальненим типом
- •7.1.2. Безпосередньо задане перевизначення узагальненої функції
- •7.1.3. Шаблонна функція з двома узагальненими типами
- •7.1.4. Механізм перевизначення специфікації шаблону функції
- •7.1.5. Використання стандартних параметрів у шаблонних функціях
- •7.1.6. Обмеження, які застосовуються при використанні узагальнених функцій
- •7.1.7. Приклад створення узагальненої функції abs()
- •7.2. Поняття про узагальнені класи
- •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.5. Застосування до функцій-членів класу модифікаторів const і mutable
Функції-члени класу можуть бути оголошені з використанням модифікатора const. Це означає, що з покажчиком this у цьому випадку необхідно звертатися як з const-покажчиком. Іншими словами, const-функція не може модифікувати об'єкт, для якого вона викликана. Окрім цього, const-об'єкт не може викликати не const-функцію-члена класу. Але const-функцію-члена можуть викликати як const-, так і не const-об'єкти.
Щоб визначити функцію як const-члена класу, використовується формат, який подано у наведеному нижче прикладі:
class aType { // Оголошення класового типу
int some_var;
public:
int Fun1() const; // const-функція-член
};
Як бачите, модифікатор const розташовується після оголошення переліку параметрів функції.
Мета оголошення функції як const-члена класу – не допустити модифікацію об'єкта, який її викликає. Для розуміння сказаного розглянемо наведену нижче програму.
Демонстрація механізму використання const-функцій-членів класу
// Ця програма не відкомпілюється.
#include <iostream> // Для потокового введення-виведення
using namespace std; // Використання стандартного простору імен
class Demo {
int c;
public:
int Put() const;
return c; // Все гаразд
}
void Set(int x) const
{
c = x; // Помилка!
};
int main()
{
Demo Obj;
Obj.Set(1900);
cout << Obj.Put();
getch(); return 0;
}
Ця програма не відкомпілюється, оскільки функцію Set() оголошено як const-член. Це означає, що їй не дозволено модифікувати той об'єкт, який її викликає. Її спроба змінити вміст змінної c призводить до виникнення помилки. На відміну від функції Set(), функція Put() не намагається модифікувати змінну c, і тому вона абсолютно прийнятна.
Можливі ситуації, коли потрібно, щоб const-функція могла змінити один або декілька членів класу, але ніяк не могла вплинути на інші. Це можна реалізувати за допомогою модифікатора mutable, який перевизначає атрибут функції const. Іншими словами, mutable-член може бути модифікований const-функцією-членом. Розглянемо такий приклад.
Демонстрація механізму використання модифікатора mutable
#include <iostream> // Для потокового введення-виведення
using namespace std; // Використання стандартного простору імен
class Demo {
mutable int c;
int d;
public:
int Put() const { return c; } // Все гаразд
void Set(int x) const
{
c = x; // тепер все гаразд
};
void setJ(int x) const // Наступна функція не відкомпілюється.
{
d = x; // Це, як і раніше, неправильно!
};
}
int main()
{
Demo Obj;
Obj.Set(1900);
cout << Obj.Put();
getch(); return 0;
}
У цьому коді програми закритий член-даних c визначено з використанням модифікатора mutable, тому його можна змінити за допомогою функції Set(). Проте змінна d не є mutable-членом, тому функції Set() не дозволяється модифікувати його значення.
11.6. Використання explicit-конструкторів
У мові програмування C++ визначено ключове слово explicit, яке застосовується для оброблення спеціальних ситуацій, наприклад, при використанні конструкторів певних типів. Щоби зрозуміти призначення специфікатора explicit, спочатку розглянемо наведену нижче програму.
Демонстрація механізму використання специфікатора explicit
#include <iostream> // Для потокового введення-виведення
using namespace std; // Використання стандартного простору імен
class myClass { // Оголошення класового типу
int a;
public:
myClass(int x) { a = x; }
int Put() { return a; }
};
int main()
{
myClass ObjA(4);
cout << ObjA.Put();
getch(); return 0;
}
У цьому коді програми конструктор класу myClass приймає один параметр. Зверніть увагу на те, який оголошено об'єкт ObjA у функції main(). Значення 4, що задається у круглих дужках після імені ObjA, є аргументом, який передається параметру x конструктора myClass(), а параметр x, своєю чергою, використовується для ініціалізації члена a. Саме таким способом ми ініціалізуємо члени класу, як це було показано на початку цього навчального посібника. Проте існує і альтернативний варіант ініціалізації. Наприклад, у процесі виконання такої настанови член класу a також набуде значення 4.
myClass ObjA = 4; // Цей формат ініціалізації перетвориться у формат myClass(4).
Як зазначено у коментарі, цей формат ініціалізації автоматично перетвориться у виклик конструктора класу myClass, а число 4 використано як аргумент. Іншими словами, попередня настанова обробляється компілятором так, як вона була записана:
myClass ObjA(4);
У загальному випадку завжди, якщо у Вас є конструктор, який приймає тільки один аргумент, то для ініціалізації змінних об'єкта можна використовувати будь-який з форматів: або ObjA(x), або ObjA = x. Йдеться про те, що при створенні конструктора класу з одним аргументом Ви опосередковано створите перетворення з типу аргумента в тип цього класу.
Якщо програмісту не потрібно, щоб таке неявне перетворення мало місце, то можна запобігти цьому за допомогою специфікатора explicit. Ключове слово explicit застосовується тільки до конструкторів. Конструктор, визначений за допомогою специфікатора explicit, буде задіяний тільки у тому випадку, якщо для ініціалізації членів-даних класу використовується звичайний синтаксис конструктора. Ніяких автоматичних перетворень виконано не буде. Наприклад, оголошуючи конструктор класу myClass з використанням специфікатора explicit, ми, тим самим, відміняємо підтримку автоматичного перетворення типів. У цьому варіанті визначення класу функція myClass() оголошується як explicit-конструктор.
#include <iostream> // Для потокового введення-виведення
using namespace std; // Використання стандартного простору імен
class myClass { // Оголошення класового типу
int a;
public:
explicit myClass(int x) { a = x; }
int Put() { return a; }
};
Тепер буде дозволено до застосування тільки конструктори, які задаються в такому форматі:
myClass ObjA(110);
Потреба неявного перетворення конструктора. Автоматичне перетворення з типу аргумента конструктора у виклик конструктора саме по собі має цікаві наслідки. Розглянемо, наприклад, такий код програми.
Демонстрація механізму використання неявного перетворення конструктора
#include <iostream> // Для потокового введення-виведення
using namespace std; // Використання стандартного простору імен
class myClass { // Оголошення класового типу
int n;
public:
myClass(int c) { n = c; }
int Put() { return n; }
};
int main()
{
myClass Obj(10);
cout << Obj.Put() << endl; // Відображає 10
// Тепер використовуємо неявне перетворення для присвоєння нового значення.
Obj = 1000;
cout << Obj.Put() << endl; // Відображає 1000
getch(); return 0;
}
Зверніть увагу на те, що нове значення присвоюється об'єкту Obj за допомо гою такої настанови:
Obj = 1000;
Використання такого формату можливе завдяки опосередкованому перетворенню з типу int в тип myClass, яке створюється конструктором myClass(). Звичайно ж, якби конструктор myClass() було оголошено за допомогою специфікатора explicit, то попередня настанова не могла б виконатися.
