Розділ №9. Підсумковий тест

  Юрій  | 

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

 165

У цьому розділі ми розглянули перевантаження операторів, перевантаження операцій конвертації типів даних, а також кілька тем, пов’язаних з конструктором копіювання. Пора закріпити отримані знання.

Теорія

Перевантаження оператора — це специфічне перевантаження функції, яка дозволяє використовувати оператори з об’єктами користувацьких класів. При перевантаженні операторів їх функціонал і призначення слід зберігати максимально наближеним до їх початкового застосування. Якщо суть застосовуваного оператора з об’єктами користувацьких класів інтуїтивно незрозуміла, то краще використовувати функцію з ім’ям, замість перевантаження оператора.

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

   Перевантаження операторів присвоювання (=), індексу ([]), виклику функції (()) або вибору члена (->) виконуйте через методи класу.

   Перевантаження унарних операторів виконуйте через методи класу.

   Перевантаження бінарних операторів, які змінюють свій лівий операнд (наприклад, оператор +=) виконуйте через методи класу.

   Перевантаження бінарних операторів, які не змінюють свій лівий операнд (наприклад, оператор +) виконуйте через звичайні або дружні функції.

Перевантаження операцій конвертації типів даних використовується для явного або неявного перетворення об’єктів користувацького класу в інший тип даних.

Конструктор копіювання — це особливий тип конструктора, який використовується для ініціалізації об’єкта іншим об’єктом того ж класу. Конструктори копіювання використовуються в прямій/uniform-ініціалізації об’єктів об’єктами того ж типу, копіюючій ініціалізації (Fraction f = Fraction(7,4)) і при передачі або поверненні параметрів по значенню.

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

Конструктори вважаються конструкторами конвертації за замовчуванням. Це означає, що компілятор використовуватиме їх для неявної конвертації об’єктів інших типів даних в об’єкти вашого класу. Ви можете уникнути цього, використовуючи ключове слово explicit. Ви також можете видалити функції всередині свого класу, включаючи конструктор копіювання і перевантажений оператор присвоювання, якщо це необхідно. І якщо пізніше в програмі викликатиметься видалена функція, то компілятор видасть помилку.

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

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

Тест

Завдання №1

Припустимо, що Square — це клас, а square — це об’єкт цього класу. Який спосіб перевантаження краще використати для наступних операторів?

   square + square

   -square

   std::cout << square

   square = 7;

Відповідь №1

   Перевантаження бінарного оператора + краще виконувати через звичайну/дружню функцію.

   Перевантаження унарного оператора - краще виконувати через метод класу.

   Перевантаження оператора << повинне виконуватися через звичайну/дружню функцію.

   Перевантаження оператора = повинне виконуватися через метод класу.

Завдання №2

Напишіть клас Average, який обчислюватиме середнє значення всіх переданих йому цілих чисел. Використовуйте два члени: перший повинен бути типу int32_t і використовуватися для обчислення суми всіх переданих чисел, другий повинен бути типу int8_t і використовуватися для обчислення кількості переданих чисел. Щоб знайти середнє значення, потрібно розділити суму на кількість.

a) Наступний код функції main():

Повинен видавати наступний результат:

5
7
11
6
7
7

Відповідь №2.а)

b) Чи потрібний цьому класу явний конструктор копіювання чи оператор присвоювання?

Відповідь №2.b)

Ні. Використання конструктора копіювання і перевантаженого оператора присвоювання, які надані компілятором за замовчуванням, тут буде достатньо.

Завдання №3

Напишіть свій власний клас-масив цілих чисел IntArray (не використовуйте std::array чи std::vector). Користувачі повинні передавати розмір масиву при створенні об’єкта цього класу, а сам масив (змінна-член) повинен виділятися динамічно. Використовуйте стейтменти assert для перевірки переданих значень, а також свій конструктор копіювання і перевантаження оператора присвоювання, якщо це необхідно, щоб наступний код:

Видавав наступний результат:

6 7 3 4 5 8
6 7 3 4 5 8

Відповідь №3

Завдання №4

Значення типу з плаваючою крапкою — це число з десятковим дробом, де кількість цифр після крапки (дробова частина) може змінюватися. Значення типу з фіксованою крапкою — це число з дробом, де дробова частина (після крапки) фіксована.

Вам потрібно написати клас для реалізації значень типу з фіксованою крапкою з двома цифрами після крапки (наприклад, 11.47, 5.00 або 1465.78). Діапазон класу повинен бути від -32768.99 до 32767.99, в дробовій частині можуть бути будь-які дві цифри, але не допускайте проблем з точністю.

a) Якого типу даних змінну-член слід використовувати для реалізації значень типу з фіксованою крапкою з двома цифрами після крапки? (Обов’язково прочитайте відповідь, перш ніж приступати до виконання наступного завдання)

Відповідь №4.а)

Існує кілька способів реалізації значень типу з фіксованою крапкою. Оскільки це той же тип з плаваючою крапкою (крім того, що кількість цифр після крапки є фіксованою), то використання типу float або типу double може здатися очевидним рішенням. Але значення типу з плаваючою крапкою мають проблеми з точністю. З фіксованою крапкою ми можемо перебрати всі можливі числа, які можуть перебувати в дробовій частині значення (в нашому випадку, від .00 до .99), тому використання типу даних з помилками в точності не є хорошим вибором.

Краще рішення: Використовуйте тип int16_t signed для зберігання цілої частини значення і int8_t signed для зберігання дробової частини значення.

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

Видавав наступний результат:

37.58
-3.09
-4.07
-5.07
-0.03
-0.03

Підказка: Для виводу значення конвертуйте його в тип double, використовуючи оператор static_cast.

Відповідь №4.b)

c) Тепер додайте конструктор, який прийматиме значення типу double. Ви можете округлити цілу частину (зліва від крапки) за допомогою функції round() (яка знаходиться в заголовку cmath).

Підказки:

   Ви можете отримати цілу частину від числа типу double шляхом конвертації числа типу double в число типу int.

   Для переміщення однієї цифри вліво від крапки використовуйте множення на 10. Для переміщення двох цифр використовуйте множення на 100.

Наступний код функції main():

Повинен видавати наступний результат:

0.03
-0.03
4.01
-4.01

Відповідь №4.c)

d) Виконайте перевантаження наступних операторів: ==, >>, (унарний) і + (бінарний).

Наступна програма:

Повинна видавати наступний результат:

true
true
true
true
true
true
true
true
-0.48
0.48
Enter a number: 5.678
You entered: 5.68

Підказка: Для виконання перевантаження оператора >> використовуйте конструктор з параметром типу double для створення анонімного об’єкта класу FixedPoint, а потім присвойте цей об’єкт параметру функції перевантаження оператора >>.

Відповідь №4.d)

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

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

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

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