Урок №131. Класи і const

  Юрій  | 

  Оновл. 12 Лют 2021  | 

 62

На уроці №40 ми дізналися, що фундаментальні типи даних (int, double, char і т.д.) можна зробити константними, використовуючи ключове слово const, і що всі константні змінні повинні бути ініціалізовані під час оголошення. У випадку з константними фундаментальними типами даних ініціалізація може бути копіюючою, прямою або uniform:

Константні об’єкти класів

Об’єкти класів можна зробити константними (використовуючи ключове слово const). Ініціалізація виконується через конструктори класів:

Як тільки константний об’єкт класу ініціалізується через конструктор, то будь-яка спроба модифікувати змінні-члени об’єкта заборонена, оскільки це порушує принципи константності об’єкта. Забороняється як зміна змінних-членів напряму (якщо вони є public), так і виклик методів (сеттерів), за допомогою яких можна встановити значення змінним-членам. Розглянемо наступний клас:

Рядки №16-17 спровокують помилки компіляції, так як вони порушують принципи константності об’єкта, намагаючись напряму змінити змінну-член, викликаючи для цього сеттер.

Константні методи класів

Тепер розглянемо наступний рядок коду:

Дивно, але це також викличе помилку компіляції, хоча метод getValue() не робить нічого для модифікації змінної-члена! Виявляється, константні об’єкти класу можуть явно викликати тільки константні методи класу, а getValue() не вказано, як константний метод. Константний метод — це метод, який гарантує, що не змінюватиме об’єкт або не викликатиме неконстантні методи класу (оскільки вони можуть змінити об’єкт).

Щоб зробити getValue() константним, потрібно просто додати ключове слово const до прототипу функції після списку параметрів, але перед тілом функції:

Тепер getValue() є константним методом. Це означає, що ми можемо викликати його через будь-який константний об’єкт.

Для методів, визначених поза тілом класу, ключове слово const має використовуватися як в прототипі функції (в тілі класу), так і у визначенні функції:

Крім того, будь-який константний метод, який намагається змінити змінну-член або викликати неконстантний метод класу, також призведе до помилки компіляції, наприклад:

У цьому прикладі метод resetValue() був встановлений константним, але він намагається змінити значення m_value. Це викличе помилку компіляції.

Зверніть увагу, конструктори не можуть бути константними. Це пов’язано з тим, що вони повинні мати можливість ініціалізувати змінні-члени класу, а константний конструктор цього не може зробити. Тому в мові С++ константні конструктори заборонені.

Варто відзначити, що константний об’єкт класу може викликати конструктор, який буде ініціалізувати всі або деякі змінні-члени, або ж не буде їх ініціалізувати взагалі!

Правило: Робіть всі ваші методи, які не змінюють дані об’єкта класу, константними.

Константні посилання і класи

Ще одним способом створення константних об’єктів є передача об’єктів в функцію по константному посиланню.

На уроці №104 ми розглянули переваги передачі аргументів по константному посиланню, ніж по значенню. Якщо коротко, то передача аргументів по значенню створює копію значення (що є повільним процесом). Більшість часу нам не потрібна копія, а посилання вже вказує на вихідний аргумент і є більш ефективним, так як уникає створення і використання непотрібної копії. Ми зазвичай робимо посилання константним для гарантії того, що функція не змінить значення аргументу і зможе працювати з r-values (наприклад, з літералами).

Чи можете ви визначити, що не так з наступним кодом?

Відповідь полягає в тому, що всередині функції printDate(), об’єкт date розглядається як константний. І через цей константний date ми викликаємо методи getDay(), getMonth() і getYear(), які є неконстантними. Оскільки ми не можемо викликати неконстантні методи через константні об’єкти, то тут ми отримаємо помилку компіляції.

Рішення просте — зробити getDay(), getMonth() і getYear() константними:

Тепер в функції printDate() константний date зможе викликати getDay(), getMonth() і getYear().

Перевантаження константних і неконстантних функцій

Хоча це робиться не дуже часто, але функцію можна перевантажити таким чином, щоб мати константну і неконстантну версії однієї і тієї ж функції:

Константна версія функції буде викликатися для константних об’єктів, а неконстантна версія буде викликатися для неконстантних об’єктів:

Перевантаження методу і його поділ на константну і неконстантну версії зазвичай виконується, коли значення, що повертається, має відрізнятися по константності (коли потрібно константа, і коли — ні). У прикладі, наведеному вище, неконстантна версія getValue() буде працювати тільки з неконстантними об’єктами, але ця версія більш гнучка, так як ми можемо використовувати її як для читання, так і для запису m_value (що ми, власне, і робимо, присвоюючи рядок Hello!).

Але коли ми не змінюємо дані об’єкта класу, то тоді викликається константна версія getValue().

Висновки

Будь-який метод, який не змінює дані об’єкта класу, повинен бути const!

Оцінити статтю:

1 Зірка2 Зірки3 Зірки4 Зірки5 Зірок (1 оцінок, середня: 5,00 з 5)
Loading...

Залишити відповідь

Ваш E-mail не буде опублікований. Обов'язкові поля відмічені *