Я маю свою відповідь у формі бесіди, щоб її краще прочитати:
Навіщо нам потрібні віртуальні функції?
Через поліморфізм.
Що таке поліморфізм?
Те, що базовий покажчик також може вказувати на об'єкти похідного типу.
Як це визначення поліморфізму призводить до необхідності віртуальних функцій?
Ну, через раннє зв’язування .
Що таке раннє зв’язування?
Рання прив'язка (прив'язка часу компіляції) в C ++ означає, що виклик функції фіксується перед виконанням програми.
Тому...?
Отже, якщо ви використовуєте базовий тип як параметр функції, компілятор розпізнає лише базовий інтерфейс, і якщо ви будете називати цю функцію будь-якими аргументами з похідних класів, вона відрізається, а це не те, що ви хочете статися.
Якщо це не те, що ми хочемо, щоб це сталося, чому це дозволено?
Бо нам потрібен поліморфізм!
Яка користь від поліморфізму тоді?
Ви можете використовувати вказівник базового типу як параметр однієї функції, а потім під час виконання вашої програми ви можете отримати доступ до кожного з похідних інтерфейсів типу (наприклад, їхніх членів) без будь-яких проблем, використовуючи перенаправлення цього єдиного базовий покажчик.
Я досі не знаю, які віртуальні функції хороші для ...! І це було моє перше питання!
ну, це тому, що ви занадто рано поставили своє запитання!
Навіщо нам потрібні віртуальні функції?
Припустимо, що ви викликали функцію з базовим вказівником, який мав адресу об'єкта з одного з його похідних класів. Як ми вже говорили про це вище, під час виконання цей покажчик стає відхиленим, на сьогоднішній день настільки хорошим, проте ми очікуємо, що метод (== членська функція) "з нашого похідного класу" буде виконаний! Однак той самий метод (той, що має той самий заголовок) вже визначений у базовому класі, тож чому ваша програма повинна турбуватися вибирати інший метод? Іншими словами, я маю на увазі, як ви можете відрізнити цей сценарій від того, що ми звикли бачити зазвичай?
Коротка відповідь - це "функція віртуального члена в базі", а трохи довша відповідь полягає в тому, що "на цьому кроці, якщо програма бачить віртуальну функцію в базовому класі, вона знає (розуміє), що ви намагаєтеся використовувати поліморфізм "і так переходить до похідних класів (використовуючи v-таблицю , форму пізнього зв'язування), щоб знайти інший метод з тим же заголовком, але з -очікувано - іншою реалізацією.
Чому інша реалізація?
Ви, рукоятка, голова! Іди читай гарну книгу !
Гаразд, чекайте, почекайте, почекайте, чому б хто не турбувався використовувати базові вказівники, коли він / вона може просто використовувати вказівники похідного типу? Ви будете суддею, чи вартий весь цей головний біль? Подивіться на ці два фрагменти:
// 1:
Parent* p1 = &boy;
p1 -> task();
Parent* p2 = &girl;
p2 -> task();
// 2:
Boy* p1 = &boy;
p1 -> task();
Girl* p2 = &girl;
p2 -> task();
Гаразд, хоча я думаю, що 1 все ще краще, ніж 2 , ви можете написати також 1 :
// 1:
Parent* p1 = &boy;
p1 -> task();
p1 = &girl;
p1 -> task();
і крім того, ви повинні знати, що це поки що лише надумане використання всіх речей, які я вам до цього часу пояснював. Замість цього, припустимо, наприклад, ситуацію, в якій у вас була функція у вашій програмі, яка використовувала методи відповідно до кожного з похідних класів відповідно (getMonthBenefit ()):
double totalMonthBenefit = 0;
std::vector<CentralShop*> mainShop = { &shop1, &shop2, &shop3, &shop4, &shop5, &shop6};
for(CentralShop* x : mainShop){
totalMonthBenefit += x -> getMonthBenefit();
}
Тепер спробуйте переписати це, без головних болів!
double totalMonthBenefit=0;
Shop1* branch1 = &shop1;
Shop2* branch2 = &shop2;
Shop3* branch3 = &shop3;
Shop4* branch4 = &shop4;
Shop5* branch5 = &shop5;
Shop6* branch6 = &shop6;
totalMonthBenefit += branch1 -> getMonthBenefit();
totalMonthBenefit += branch2 -> getMonthBenefit();
totalMonthBenefit += branch3 -> getMonthBenefit();
totalMonthBenefit += branch4 -> getMonthBenefit();
totalMonthBenefit += branch5 -> getMonthBenefit();
totalMonthBenefit += branch6 -> getMonthBenefit();
І насправді це може бути ще й надуманим прикладом!