Урок №149. Конструктор копіювання

  Юрій  | 

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

 61

Згадаймо всі типи ініціалізації, які підтримує мова C++: пряма ініціалізація, uniform-ініціалізація і копіююча ініціалізація.

Конструктор копіювання

Розглянемо приклади всіх вищенаведених ініціалізацій на практиці, використовуючи наступний клас Drob:

Ми можемо виконати пряму ініціалізацію:

В C++11 ми можемо виконати uniform-ініціалізацію:

І, нарешті, ми можемо виконати копіюючу ініціалізацію:

З прямою ініціалізацією і uniform-ініціалізацією створюваний об’єкт безпосередньо ініціалізується. Однак з копіюючою ініціалізацією справи йдуть трохи складніше. Ми розглянемо це детально на наступному уроці. Але перед цим нам ще потрібно де в чому розібратися.

Розглянемо наступну програму:

Результат виконання програми:

6/7

Розглянемо детально, як працює ця програма.

З об’єктом sixSeven виконується звичайна пряма ініціалізація, яка призводить до виклику конструктора Drob(int, int). Тут немає ніяких сюрпризів. А ось ініціалізація об’єкта dCopy також є прямою ініціалізацією, але який конструктор викликається тут? Відповідь — конструктор копіювання.

Конструктор копіювання — це особливий тип конструктора, який використовується для створення нового об’єкта через копіювання існуючого. І, як у випадку з конструктором за замовчуванням, якщо ви не надасте конструктор копіювання для своїх класів самостійно, то мова C++ створить public-конструктор копіювання автоматично. Оскільки компілятор мало знає про ваш клас, то за замовчуванням створений конструктор копіювання використовуватиме почленну ініціалізацію. Почленна ініціалізація означає, що кожен член об’єкта-копії ініціалізується безпосередньо з члена об’єкта-оригіналу. Тобто в прикладі, наведеному вище, dCopy.m_numerator матиме значення sixSeven.m_numerator (6), а dCopy.m_denominator дорівнюватиме sixSeven.m_ denominator (7).

Так само, як ми можемо явно визначити конструктор за замовчуванням, так само ми можемо явно визначити і конструктор копіювання. Конструктор копіювання виглядає наступним чином:

Результат виконання програми:

Copy constructor worked here!
6/7

Конструктор копіювання у вищенаведеному прикладі використовує почленну ініціалізацію і функціонально еквівалентний конструктору за замовчуванням, за винятком того, що ми додали стейтмент виводу, в якому вказали текст (спрацював конструктор копіювання).

Запобігання створенню копій об’єктів

Ми можемо запобігти створенню копій об’єктів наших класів, зробивши конструктор копіювання закритим:

Тут ми отримаємо помилку компіляції, так як dCopy повинен використовувати конструктор копіювання, але він не бачить його, оскільки конструктор копіювання є закритим.

Конструктор копіювання можна проігнорувати

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

Спочатку ініціалізується анонімний об’єкт Drob, який призводить до виклику конструктора Drob(int, int). Потім цей анонімний об’єкт використовується для ініціалізації об’єкта sixSeven класу Drob. Оскільки анонімний об’єкт є об’єктом класу Drob, як і sixSeven, то тут повинен викликатися конструктор копіювання, вірно?

Запустіть цю програму самостійно. Очікуваний результат:

Copy constructor worked here!
6/7

Реальний результат:

6/7

Чому наш конструктор копіювання не спрацював?

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

З цієї причини в таких випадках компілятору дозволяється відмовитися від виклику конструктора копіювання і просто виконати пряму ініціалізацію. Цей процес називається елізією.

Тому, навіть якщо ви напишите:

Компілятор може змінити це на:

В останньому випадку з прямою ініціалізацією потрібен буде виклик тільки одного конструктора (Drob(int, int)). Зверніть увагу, у випадках, коли використовується елізія, будь-які стейтменти в тілі конструктора копіювання не виконуються, навіть якщо вони мають побічні ефекти (наприклад, виводять щось на екран)!

Нарешті, якщо ви робите свій конструктор копіювання закритим, то будь-яка ініціалізація, яка використовує цей закритий конструктор копіювання, призведе до помилок компіляції, навіть якщо конструктор копіювання буде проігнорований!

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

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

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

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