Віртуальні методи
Ми вже пройшли наслідування класів. І дізналися, що дочірній клас може наслідувати публічні та захищенні змінні та методи батьківського класу. Також, ми навчилися використовувати перевизначення методів батьківського класу (коли створюємо метод з таким же ім’ям в дочірньому класі). Але розгляньмо декілька нюансів, які можуть вам трапитися.
Погляньте на наступний код. Як ви вважаєте, що виведе наступна програма?
#include <iostream>
class Father
{
public:
void Display()
{
std::cout << "Father" << std::endl;
}
void Show()
{
Display();
}
};
class Child : public Father
{
public:
void Display()
{
std::cout << "Child" << std::endl;
}
};
int main()
{
Child Child;
Child.Display();
Child.Show();
}
Ось результат виконання програми:
Child
Father
Розберемося, що ж тут сталося. Клас наслідує клас Отже, методи та стають його частиною. При цьому метод ми перевизначили. І при його виклику в нас вивелося слово
Метод «Show» ми не перевизначали. І тому, він використовує інформацію з батьківського класу. Тобто, викликає метод саме з батьківського класу. Через це ми бачимо виведення слова Тобто, метод викликає найближчий до нього метод
Думаю у вас виникає думка «то тепер мені потрібно перевизначити ще метод в дочірньому класі, щоб вивести перевизначений метод Звісно, що ні! Бо тоді толку з наслідування не буде від слова взагалі.
Якщо ви хочете, щоб під час виклику з дочірнього класу батьківські методи використовували перевизначені дочірні методи, то в батьківському класі ці методи мають бути віртуальними.
Щоб метод став віртуальним потрібно при його створенні вказати ключове слово Тоді, створення методу виглядатиме так:
virtual тип_методу ім’я_методу(){};
Важливо! Віртуальними мають бути ті методи, які ми будемо перевизначати!
Також, ваші методи можуть мати аргументи. Не забувайте про це.
Тобто, в батьківському класі нашої програми віртуальним має бути метод
#include <iostream>
class Father
{
public:
virtual void Display()
{
std::cout << "Father" << std::endl;
}
void Show()
{
Display();
}
};
class Child : public Father
{
public:
void Display()
{
std::cout << "Child" << std::endl;
}
};
int main()
{
Father Father;
Father.Display();
Father.Show();
Child Child;
Child.Display();
Child.Show();
}
І результатом програми буде:
Father
Father
Child
Child
Зверніть увагу, що при використанні методів з батьківським класом ми отримали цілком очікуваний результат. А, при використанні дочірнього класу результат змінився.
Це все через те, що тепер метод
Також, зауважу, що це стосується вкладених методів. Тобто, тих які використовуються в середині інших! Но все ж таки, краще за все, робити всі методи віртуальними, якщо ви вже плануєте їх змінювати.
Варто розуміти, що за допомогою перевизначення методів ви можете отримати дві версії одного і того ж методу (батьківську та дочірню). Якщо в дочірньому класі ви хочете зберегти можливість використовувати ці дві версії, то робити віртуальний метод не варто.
Вам потрібно буде обгорнути (помістити) цей метод в середину іншого методу. Таким чином, ви будете мати можливість перевизначити батьківський метод та використати нову версію цього методу (з першої програми метод
Це альтернатива створенню батьківського класу в середині дочірнього класу.
Отже, зробимо такі висновки:
1. Якщо ми в дочірньому класі хочемо перевизначити метод, при цьому, має використовуватися саме перевизначена (дочірня) версія, то в батьківському класі цей метод має бути віртуальним. Таким чином, ви завчасно уникнете проблем.
2. Якщо ж вам потрібно залишити батьківську версію методу і при цьому перевизначити цей же метод, тобто, отримати дві версії одного методу, то в батьківському класі створюємо додатковий метод в який поміщаємо наш метод (в нашому прикладі це був метод