Хоча мова C++ автоматично надає деструктори для ваших класів, якщо ви не надаєте їх самостійно, все ж іноді ви можете зробити це самі.
Віртуальні деструктори
При роботі зі спадкуванням ваші деструктори завжди повинні бути віртуальними. Розглянемо наступний приклад:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
#include <iostream> class Parent { public: ~Parent() // примітка: Деструктор не віртуальний { std::cout << "Calling ~Parent()" << std::endl; } }; class Child: public Parent { private: int* m_array; public: Child(int length) { m_array = new int[length]; } ~Child() // примітка: Деструктор не віртуальний { std::cout << "Calling ~Child()" << std::endl; delete[] m_array; } }; int main() { Child *child = new Child(7); Parent *parent = child; delete parent; return 0; } |
Оскільки parent
є вказівником класу Parent, то при його знищенні компілятор дивитиметься, чи є деструктор класу Parent віртуальним. Оскільки це не так, то компілятор викличе тільки деструктор класу Parent.
Результат виконання програми:
Calling ~Parent()
Проте, нам потрібно, щоб delete викликав деструктор класу Child (який, в свою чергу, викликатиме деструктор класу Parent), інакше m_array
не буде видалений. Це можна виконати, зробивши деструктор класу Parent віртуальним:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
#include <iostream> class Parent { public: virtual ~Parent() // примітка: Деструктор віртуальний { std::cout << "Calling ~Parent()" << std::endl; } }; class Child: public Parent { private: int* m_array; public: Child(int length) { m_array = new int[length]; } virtual ~Child() // примітка: Деструктор віртуальний { std::cout << "Calling ~Child()" << std::endl; delete[] m_array; } }; int main() { Child *child = new Child(7); Parent *parent = child; delete parent; return 0; } |
Результат виконання програми:
Calling ~Child()
Calling ~Parent()
Правило: При роботі зі спадкуванням ваші деструктори повинні бути віртуальними.
Віртуальне присвоювання
Оператор присвоювання можна зробити віртуальним. Однак, на відміну від деструктора, віртуальне присвоювання не завжди є хорошою ідеєю. Чому? Це вже виходить за рамки цього уроку. Отже, для збереження простоти в вашому коді, не рекомендується використовувати віртуальне присвоювання.
Ігнорування віртуальних функцій
У мові С++ ми можемо ігнорувати виклик перевизначень. Наприклад:
1 2 3 4 5 6 7 8 9 10 11 |
class Parent { public: virtual const char* getName() { return "Parent"; } }; class Child: public Parent { public: virtual const char* getName() { return "Child"; } }; |
Тут ми хочемо, щоб посилання класу Parent на об’єкт класу Child викликало Parent::getName() замість Child::getName(). Щоб це зробити, потрібно просто використати оператор дозволу області видимості:
1 2 3 4 5 6 7 8 9 10 |
#include <iostream> int main() { Child child; Parent &parent = child; // Виклик Parent::GetName() замість перевизначення Child::GetName() std::cout << parent.Parent::getName() << std::endl; } |
Ви, швидше за все, не будете використовувати це дуже часто, але знати про це варто.