Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Шпора II Sem.doc
Скачиваний:
0
Добавлен:
01.04.2025
Размер:
679.94 Кб
Скачать

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, то попередня настанова не могла б виконатися.