Учебные вопросы
1.1. Механизм наследования
1.2. Механизм наследования
1.3. Механизм наследования
1.4. Механизм наследования
2.1. Одиночное наследование
2.2. Одиночное наследование
2.3. Одиночное наследование
2.4. Одиночное наследование. Спецификация классов
2.5. Одиночное наследование. Конструкторы
2.6. Одиночное наследование. Конструкторы
2.7. Одиночное наследование. Конструкторы
2.8. Одиночное наследование. Деструкторы
2.9. Одиночное наследование. Деструкторы
2.10. Одиночное наследование. Деструкторы
2.11. Одиночное наследование. Объявление, создание и разрушение объектов
2.12. Одиночное наследование. Объявление, создание и разрушение объектов
2.13. Одиночное наследование. Объявление, создание и разрушение объектов
2.14. Одиночное наследование. Объявление, создание и разрушение объектов
3.1. Множественное наследование
3.2. Множественное наследование
3.3. Множественное наследование
3.4. Множественное наследование
3.5. Множественное наследование. Спецификация классов
3.6. Множественное наследование. Спецификация классов
3.7. Множественное наследование. Спецификация классов
3.8. Множественное наследование. Конструкторы
3.9. Множественное наследование. Конструкторы
3.10. Множественное наследование. Конструкторы
3.11. Множественное наследование. Конструкторы
3.12. Множественное наследование. Деструкторы
3.13. Множественное наследование. Деструкторы
3.14. Множественное наследование. Деструкторы
3.15. Множественное наследование. Деструкторы
3.16. Множественное наследование. Деструкторы
3.17. Множественное наследование. Объявление, создание и разрушение объектов
3.18. Множественное наследование. Объявление, создание и разрушение объектов
3.19. Множественное наследование. Объявление, создание и разрушение объектов
3.20. Множественное наследование. Объявление, создание и разрушение объектов
Рекомендуемые источники по теме лекции
470.67K
Category: programmingprogramming

ПрогрС++Лек08

1.

Кафедра «Информационные и вычислительные системы»
Дисциплина «Программирование (С++)»
Раздел 5. Наследование
Лекция 8
Механизмы наследования
Профессор кафедры, дтн, доцент
Хетчиков Дмитрий Михайлович
1

2. Учебные вопросы

1) Механизм наследования
2) Одиночное наследование
спецификация классов;
конструкторы;
деструкторы;
объявление, создание и разрушение объектов
3) Множественное наследование
спецификация классов;
конструкторы;
деструкторы;
объявление, создание и разрушение объектов
2

3. 1.1. Механизм наследования

Наследование — один из основополагающих принципов объектно-ориентированного программирования
(ООП) в C++. Оно позволяет классу (подклассу, производному классу или дочернему классу) использовать
переменные и методы другого класса (суперкласса, базового класса или родительского класса) как свои
собственные.
Ключевые термины:
•Базовый класс (суперкласс, родительский класс) — класс, от которого наследуются данные или методы.
•Производный класс (подкласс, дочерний класс) — класс, который наследует данные и методы базового
класса.
•Наследование структурирует код и позволяет повторно использовать его, ускоряя разработку.
Особенности наследования:
•Приватные переменные и методы не наследуются. Например, если в базовом классе есть приватная
переменная pincode, она не будет доступна в производном классе.
•Конструкторы и деструкторы не наследуются, но вызываются при инициализации объекта дочернего
класса:
• конструкторы вызываются иерархически (от базового класса к последнему производному);
• деструкторы вызываются в обратном порядке.
•Виртуальное наследование (virtual inheritance) предотвращает появление множественных объектов
базового класса в иерархии наследования. Пример: class Computer: virtual public Device {};.
3

4. 1.2. Механизм наследования

Типы наследования в C++:
1.Публичный (public):
1. публичные (public) и защищённые (protected) данные наследуются без изменения
уровня доступа к ним;
2. пример: class Computer: public Device {};.
2.Защищённый (protected):
1. все унаследованные данные становятся защищёнными;
2. доступ к ним ограничен в производных классах.
3.Приватный (private):
1. все унаследованные данные становятся приватными (private) для производного
класса;
2. пример: class Computer: private Device {};.
4

5. 1.3. Механизм наследования

Пример кода:
cpp
#include <iostream>
using namespace std;
class Device {
public:
int serial_number = 12345678;
void turn_on() {
cout << "Device is on" << endl; }
private:
int pincode = 87654321;
Объяснение примера:
};
class Computer: public Device {}; •класс Computer наследует публичные методы и переменные класса Device;
•метод turn_on() и переменная serial_number доступны в Computer;
int main() {
Computer Computer_instance; •приватная переменная pincode недоступна в Computer.
Computer_instance.turn_on();
cout << "Serial number is: " << Computer_instance.serial_number << endl;
// cout << "Pin code is: " << Computer_instance.pincode << endl; // Ошибка компиляции
return 0;
}
5

6. 1.4. Механизм наследования

Преимущества наследования:
•структурирование кода;
•повторное использование кода;
•ускорение разработки.
Недостатки и предостережения:
•изменения в базовом классе могут затронуть все производные классы, приводя к
непредвиденным последствиям;
•сложная иерархия классов усложняет поддержку кода;
•наследование от частично реализованных классов создаёт тесную связь между
родительским и дочерним классами.
Применение:
•библиотеки и фреймворки часто предоставляют абстрактные классы с виртуальными и
реализованными методами, позволяя пользователям кастомизировать решения;
•наследование от интерфейсов (чистых абстрактных классов) обеспечивает
структурирование кода и защиту от изменений в реализующих классах.
6

7. 2.1. Одиночное наследование

Одиночное наследование — это механизм объектно-ориентированного программирования
(ООП) в C++, при котором производный (дочерний) класс наследует свойства и методы
только от одного базового (родительского) класса. Это самый простой и распространённый
вид наследования.
Основные принципы
Наследование полей и методов:
o производный класс получает все публичные (public) и защищённые (protected) члены базового
класса;
o приватные (private) члены базового класса недоступны напрямую в производном классе, но
могут быть использованы через публичные/защищённые методы базового класса.
Спецификатор доступа:
o ключевое слово public в объявлении наследования делает члены базового класса доступными
в производном классе с тем же уровнем доступа:
cpp
class Derived : public Base { ... };
o также существуют protected и private уровни наследования, изменяющие доступность
унаследованных членов.
7

8. 2.2. Одиночное наследование

Иерархия классов:
o формируется «дерево» классов, где каждый дочерний класс — это специализированная
версия родительского;
o пример иерархии: Animal (базовый класс) → Dog, Cat (производные классы).
Синтаксис
Синтаксис объявления производного класса:
cpp
class ИмяПроизводногоКласса : спецификатор_доступа BaseClass {
// Дополнительные поля и методы
};
где:
• ИмяПроизводногоКласса — название нового класса;
• спецификатор_доступа — public, protected или private;
• BaseClass — имя базового класса.
8

9. 2.3. Одиночное наследование

Пример кода
cpp
class Animal {
public:
void speak() {
std::cout << "Animal speaks" << std::endl; }
protected:
int age;
private:
std::string name;
};
class Dog : public Animal {
public:
void bark() {
std::cout << "Dog barks" << std::endl; }
};
Здесь:
класс Dog наследует метод speak() от Animal;
защищённое поле age также доступно в Dog;
приватное поле name недоступно напрямую в Dog.
Преимущества
•Переиспользование кода: общие методы и поля
выносятся в базовый класс.
•Расширяемость: легко добавлять новые функции
в производные классы.
•Иерархическая организация: улучшает структуру
кода и его читаемость.
Недостатки
•Жёсткая связь: изменение базового класса может
повлиять на все производные классы.
•Ограниченность: только один родительский класс
— нельзя комбинировать функциональность
нескольких классов (для этого используется
множественное наследование).
Итог: одиночное наследование — мощный
инструмент ООП в C++, позволяющий строить
логичные иерархии классов и избегать
дублирования кода.
9

10. 2.4. Одиночное наследование. Спецификация классов

Спецификаторы доступа при наследовании
При наследовании ключевое значение имеет спецификатор доступа. Возможные варианты:
public — все public-члены базового класса становятся public в производном классе. Это
наиболее распространённый вариант.
private — все public-члены базового класса становятся private в производном классе
(недоступны извне).
protected — public и protected члены базового класса становятся protected в производном
классе.
Спецификация классов включает:
определение полей (переменных) и методов (функций);
указание спецификаторов доступа (public, private, protected);
описание конструкторов и деструкторов;
наследование (выбор базового класса и спецификатора доступа)..
10

11. 2.5. Одиночное наследование. Конструкторы

1. Ключевой принцип: конструкторы не наследуются
Одно из важных правил C++ — конструкторы базового класса не наследуются в производный класс. Это
значит, что если в базовом классе есть конструкторы, их нужно явно «перенести» в производный класс или
использовать специальные механизмы.
2. Порядок вызова конструкторов
При создании объекта производного класса конструкторы вызываются по иерархии:
Сначала вызывается конструктор базового класса.
Затем — конструктор производного класса.
Это гарантирует корректную инициализацию всех уровней иерархии.
3. Способы передачи конструкторов базового класса в производный
Существует два основных способа обеспечить вызов конструкторов базового класса:
а) Явный вызов в списке инициализации
В конструкторе производного класса явно указать вызов конструктора базового класса с помощью списка
инициализации (:).
б) Использование using для копирования всех конструкторов
Ключевое слово using позволяет «скопировать» все конструкторы базового класса в производный без
ручного прописывания каждого.
11

12. 2.6. Одиночное наследование. Конструкторы

4. Конструкторы по умолчанию
Если в производном классе нет собственного конструктора, компилятор автоматически
сгенерирует вызов конструктора по умолчанию базового класса (если он существует).
Если конструктор по умолчанию отсутствует в базовом классе, компилятор выдаст ошибку при
попытке создать объект производного класса.
5. Конструкторы и спецификаторы доступа
Спецификатор доступа (public, protected, private) в наследовании влияет на доступность
конструкторов:
При public-наследовании конструкторы базового класса доступны для вызова в производном
классе.
При private-наследовании конструкторы базового класса становятся private в производном
классе — их можно вызвать только внутри методов производного класса.
При protected-наследовании — становятся protected.
12

13. 2.7. Одиночное наследование. Конструкторы

Важные нюансы
Если базовый класс содержит виртуальные функции, рекомендуется сделать деструктор
базового класса виртуальным для корректного удаления объектов.
При переопределении конструкторов важно следить за соответствием параметров и логики
инициализации.
Если в производном классе добавляются новые поля, их инициализация должна быть указана
в списке инициализации конструктора.
Вывод
При одиночном наследовании в C++:
конструкторы базового класса не наследуются автоматически;
порядок вызова конструкторов — от базового к производному;
можно использовать список инициализации или using для передачи конструкторов;
порядок вызова деструкторов — от производного к базовому;
спецификаторы доступа влияют на видимость и доступность конструкторов.
13

14. 2.8. Одиночное наследование. Деструкторы

Деструктор — это специальный метод класса, который вызывается автоматически при удалении
объекта. Его задача — освободить ресурсы (память, файлы, сетевые соединения и т. д.), занятые
объектом. В контексте одиночного наследования порядок вызова деструкторов имеет ключевое
значение для корректной работы программы.
Порядок вызова деструкторов
При уничтожении объекта производного класса деструкторы вызываются в обратном порядке по
сравнению с конструкторами:
1.Сначала вызывается деструктор производного (дочернего) класса.
2.Затем — деструктор базового (родительского) класса.
Важность виртуальных деструкторов
Если базовый класс содержит виртуальные функции, рекомендуется сделать его
деструктор виртуальным. Это гарантирует корректное удаление объектов производного класса
через указатель на базовый класс.
14

15. 2.9. Одиночное наследование. Деструкторы

Спецификаторы доступа и деструкторы
Спецификатор доступа (public, protected, private) при наследовании влияет на доступность
деструктора базового класса:
public-наследование: деструктор базового класса доступен для вызова.
protected-наследование: деструктор становится protected в производном классе.
private-наследование: деструктор становится private в производном классе.
Особенности работы деструкторов
1) Автоматическое удаление: деструкторы вызываются автоматически при выходе объекта из
области видимости или при явном удалении (delete).
2) Инициализация и освобождение ресурсов: если в конструкторе были выделены ресурсы
(например, память через new), в деструкторе их нужно корректно освободить (delete,
закрытие файлов и т. д.).
3) Цепочка вызовов: в глубокой иерархии наследования деструкторы вызываются
последовательно от самого производного класса к базовому.
15

16. 2.10. Одиночное наследование. Деструкторы

Типичные ошибки
1.Забытый виртуальный деструктор в базовом классе при работе с указателями.
2.Некорректное освобождение ресурсов в деструкторе (например, двойное удаление
памяти).
3.Отсутствие деструктора в классе, если он управляет ресурсами.
Вывод
•Деструкторы при одиночном наследовании вызываются в обратном
порядке относительно конструкторов.
•Для безопасного удаления объектов через указатель на базовый класс деструктор
базового класса должен быть виртуальным.
•Порядок вызова деструкторов обеспечивает корректное освобождение ресурсов на всех
уровнях иерархии.
•Спецификаторы доступа влияют на видимость деструктора, но не меняют порядок его
вызова.
16

17. 2.11. Одиночное наследование. Объявление, создание и разрушение объектов

1. Объявление объектов (классов) при одиночном наследовании
Одиночное наследование в C++ — это механизм, при котором производный (дочерний) класс
наследует свойства и методы только от одного базового (родительского) класса.
Синтаксис объявления производного класса:
cpp
class ИмяПроизводногоКласса : спецификатор_доступа BaseClass {
// Дополнительные поля и методы
};
где:
ИмяПроизводногоКласса — название нового класса;
спецификатор_доступа (public, protected, private) определяет видимость унаследованных
членов;
BaseClass — имя базового класса.
17

18. 2.12. Одиночное наследование. Объявление, создание и разрушение объектов

2. Создание объектов (экземпляров классов)
При создании объекта производного класса выполняются следующие шаги:
Вызывается конструктор базового класса — инициализирует унаследованные поля и ресурсы.
Вызывается конструктор производного класса — инициализирует собственные поля и дополняет логику.
Способы создания объектов:
Статические объекты (в стеке): объявляются напрямую в функции или блоке кода. Пример: Dog myDog;
Динамические объекты (в куче) с использованием оператора new: Dog* myDogPtr = new Dog();
Передача конструкторов базового класса:
Явный вызов в списке инициализации:
cpp
Dog::Dog() : Animal() { /* инициализация Dog */ }
Использование using для копирования всех конструкторов базового класса:
cpp
class Dog : public Animal {
public:
using Animal::Animal; // копируем все конструкторы Animal
};
18

19. 2.13. Одиночное наследование. Объявление, создание и разрушение объектов

3. Разрушение объектов (деструкторы)
При удалении объекта выполняются деструкторы в обратном порядке относительно
конструкторов:
1) Сначала вызывается деструктор производного класса — освобождает ресурсы, созданные в
Dog.
2) Затем вызывается деструктор базового класса — освобождает унаследованные ресурсы.
Особенности деструкторов:
Деструкторы не наследуются, но вызываются автоматически при уничтожении объекта.
Имя деструктора — имя класса с префиксом ~. Пример: ~Dog(), ~Animal().
Деструкторы не могут иметь аргументов и возвращаемого типа.
Для корректного удаления объектов через указатель на базовый класс деструктор базового
класса должен быть виртуальным.
Удаление объектов:
Статические объекты уничтожаются автоматически при выходе из области видимости.
Динамические объекты удаляются с помощью оператора delete: delete myDogPtr;
19

20. 2.14. Одиночное наследование. Объявление, создание и разрушение объектов

4. Ключевые моменты
Порядок инициализации: базовый класс → производный класс.
Порядок разрушения: производный класс → базовый класс.
Виртуальные деструкторы обязательны при работе с указателями на базовый класс.
Спецификаторы доступа (public, protected, private) влияют на видимость
унаследованных членов, но не меняют порядок вызова конструкторов/деструкторов.
Итог:
Понимание механизмов объявления, создания и разрушения объектов при одиночном
наследовании критически важно для корректной работы с памятью и ресурсами в C++.
20

21. 3.1. Множественное наследование

1. Суть множественного наследования
Множественное наследование позволяет классу наследовать свойства и методы от нескольких
базовых классов одновременно. Это расширяет возможности композиции поведения и данных в
производных классах.
Синтаксис:
cpp
class ПроизводныйКласс : спецификатор_доступа БазовыйКласс1,
спецификатор_доступа БазовыйКласс2,
...
спецификатор_доступа БазовыйКлассN {
// Тело класса
};
спецификатор_доступа — public, protected или private (определяет видимость унаследованных
членов).
Базовые классы перечисляются через запятую
21

22. 3.2. Множественное наследование

2. Особенности работы с множественным наследованием
Состав объекта: объект производного класса содержит подобъекты каждого из базовых
классов + собственные члены. Пример: Panda ying_yang состоит из:
• подобъекта Bear (который включает ZooAnimal);
• подобъекта Endangered;
• нестатических членов Panda.
Вызов конструкторов: конструкторы базовых классов вызываются в порядке их перечисления
в списке наследования, затем — конструктор производного класса. Пример для Panda:
• Конструктор ZooAnimal (так как Bear наследует от ZooAnimal);
• Конструктор Bear;
• Конструктор Endangered;
• Конструктор Panda.
Вызов деструкторов: происходит в обратном порядке — сначала деструктор Panda, затем
Endangered, Bear и ZooAnimal.
22

23. 3.3. Множественное наследование

3. Проблемы множественного наследования
Неоднозначность имён: если в нескольких базовых классах есть методы с одинаковыми
именами, компилятор выдаст ошибку при попытке их вызова без уточнения. Пример: если Bear
и Endangered имеют метод print(), вызов pandaObj.print() приведёт к ошибке. Решение: явное
указание класса: pandaObj.Bear::print() или pandaObj.Endangered::print().
Дублирование подобъектов: если несколько базовых классов наследуют от одного общего
предка, в производном классе будет несколько копий этого предка. Пример: Bear и Raccoon
наследуют от ZooAnimal → в Panda будет 2 копии ZooAnimal. Решение: виртуальное
наследование (ключевое слово virtual). Гарантирует единственную копию базового класса.
4. Спецификаторы доступа (public, protected, private)
public: члены базовых классов доступны как public в производном классе.
protected: члены становятся protected в производном классе (доступны только для
наследников).
private: члены становятся private в производном классе (недоступны вне класса).
23

24. 3.4. Множественное наследование

5. Практическое применение
Реализация интерфейсов: наследование от нескольких абстрактных классов.
Композиция поведения: объединение функциональности разных классов (например, iostream
наследует от istream и ostream).
Паттерны проектирования: множественное наследование используется в некоторых паттернах
(например, для комбинирования ролей объекта).
6. Ограничения и рекомендации
•Множественное наследование усложняет код и может привести к «ромбовидному
наследованию» (diamond problem) — ситуации с дублированием классов в иерархии.
•Рекомендуется использовать композицию вместо множественного наследования, если это
возможно (создание объектов базовых классов внутри производного).
•Виртуальное наследование помогает решить проблемы дублирования, но усложняет
управление памятью.
Вывод. Множественное наследование — мощная, но потенциально опасная возможность C++.
Используйте его осознанно, отдавая предпочтение композиции там, где это уместно, и применяя
виртуальное наследование для решения проблем дублирования
24

25. 3.5. Множественное наследование. Спецификация классов

1. Синтаксис множественного наследования
Класс может наследовать от нескольких базовых классов. Синтаксис выглядит следующим
образом:
cpp
class ПроизводныйКласс : спецификатор_доступа БазовыйКласс1,
спецификатор_доступа БазовыйКласс2,
...
спецификатор_доступа БазовыйКлассN {
// Тело класса
};
спецификатор_доступа (public, protected, private) определяет видимость унаследованных
членов.
Базовые классы перечисляются через запятую.
25

26. 3.6. Множественное наследование. Спецификация классов

2. Структура объекта при множественном наследовании
Объект производного класса содержит:
подобъекты каждого из базовых классов;
собственные нестатические члены, объявленные в производном классе.
Пример для Panda ying_yang:
подобъект класса Bear (который, в свою очередь, содержит подобъект ZooAnimal);
подобъект класса Endangered;
нестатические члены, объявленные в классе Panda.
3. Спецификаторы доступа (public, protected, private)
public: члены базовых классов доступны как public в производном классе.
protected: члены становятся protected в производном классе (доступны только для
наследников).
private: члены становятся private в производном классе (недоступны вне класса).
26

27. 3.7. Множественное наследование. Спецификация классов

Вывод
Спецификация классов при множественном наследовании включает:
определение порядка наследования и спецификаторов доступа;
управление структурой объекта (подобъекты, их дублирование);
контроль порядка вызова конструкторов и деструкторов;
разрешение неоднозначности имён;
использование виртуального наследования для оптимизации..
27

28. 3.8. Множественное наследование. Конструкторы

1. Порядок вызова конструкторов
При множественном наследовании конструкторы базовых классов вызываются в порядке их
объявления в списке наследования, а затем — конструктор производного класса.
Пример:
cpp
class Bear : public ZooAnimal { ... };
class Endangered { ... };
class Panda : public Bear, public Endangered { ... };
При создании объекта Panda ying_yang последовательность вызова конструкторов будет
следующей:
1) Конструктор ZooAnimal (так как Bear наследует от ZooAnimal).
2) Конструктор Bear.
3) Конструктор Endangered.
4) Конструктор Panda.
Важно: порядок вызова не зависит от порядка перечисления в списке инициализации членов.
28

29. 3.9. Множественное наследование. Конструкторы

2. Инициализация базовых классов
Инициализация базовых классов происходит через список инициализации в конструкторе
производного класса. Синтаксис:
cpp
Panda::Panda(/* аргументы */) : Bear(/* аргументы для Bear */), Endangered(/* аргументы для
Endangered */) {
// тело конструктора Panda
}
Каждый базовый класс должен быть явно указан в списке инициализации.
Аргументы для базовых классов передаются в порядке их объявления в списке наследования.
3. Виртуальное наследование и конструкторы
Если используется виртуальное наследование, порядок вызова конструкторов немного меняется:
Виртуальные базовые классы конструируются перед невиртуальными, вне зависимости от их
расположения в иерархии наследования.
Ответственность за инициализацию виртуального базового класса лежит на ближайшем
производном классе
29

30. 3.10. Множественное наследование. Конструкторы

Пример с виртуальным наследованием:
cpp
class ZooAnimal { ... };
class Bear : virtual public ZooAnimal { ... };
class Raccoon : virtual public ZooAnimal { ... };
class Panda : public Bear, public Raccoon { ... };
Здесь ZooAnimal — виртуальный базовый
класс. При создании объекта Panda:
1) Сначала вызывается конструктор
ZooAnimal (как виртуального базового
класса).
2) Затем — конструкторы Bear и Raccoon.
3) В конце — конструктор Panda.
4. Копирующие конструкторы
При множественном наследовании копирующие
конструкторы базовых классов также
вызываются в определённом порядке:
1) Копирующий конструктор первого базового
класса в списке наследования.
2) Копирующий конструктор второго базового
класса и т. д.
3) Копирующий конструктор производного
класса.
Порядок вызова:
1) Копирующий конструктор ZooAnimal.
2) Копирующий конструктор Bear.
3) Копирующий конструктор Endangered.
4) Копирующий конструктор Panda.
30

31. 3.11. Множественное наследование. Конструкторы

5. Особенности при неоднозначности
Если в нескольких базовых классах есть конструкторы с одинаковыми сигнатурами, может возникнуть
неоднозначность. В этом случае необходимо явно указать, какой конструктор базового класса использовать
в списке инициализации.
6. Конструкторы по умолчанию
Если в базовом классе нет конструктора по умолчанию, необходимо явно передать аргументы в список
инициализации производного класса. Иначе компилятор выдаст ошибку.
7. Рекомендации
Всегда указывайте все базовые классы в списке инициализации конструктора производного класса.
При виртуальном наследовании следите за порядком инициализации виртуальных базовых классов.
Учитывайте порядок вызова конструкторов при проектировании иерархии классов.
Проверяйте наличие конструкторов по умолчанию в базовых классах.
Вывод
Конструкторы при множественном наследовании вызываются в строгом порядке: сначала базовые классы (в
порядке их объявления, с приоритетом виртуальных), затем — производный класс. Правильная
инициализация через список инициализации критически важна для корректной работы программы.
31

32. 3.12. Множественное наследование. Деструкторы

1. Основной принцип
Деструкторы при множественном наследовании вызываются в обратном порядке относительно
конструкторов. Это гарантирует корректное освобождение ресурсов — от «дочернего» класса к
«родительским».
Пример иерархии:
cpp
class A { ~A() { /* ... */ } };
class B { ~B() { /* ... */ } };
class C : public A, public B { ~C() { /* ... */ } };
При уничтожении объекта класса C порядок вызова деструкторов:
1) ~C() — деструктор производного класса.
2) ~B() — деструктор второго базового класса (по порядку наследования).
3) ~A() — деструктор первого базового класса.
2. Порядок вызова деструкторов
Порядок определяется:
Порядком объявления базовых классов в списке наследования (слева направо).
Иерархией наследования — сначала разрушаются более «низкие» уровни, затем «вышестоящие».
Виртуализацией наследования — особые правила для виртуальных базовых классов.
32

33. 3.13. Множественное наследование. Деструкторы

3. Виртуальное наследование и деструкторы
При виртуальном наследовании порядок вызова деструкторов усложняется:
• Виртуальные базовые классы разрушаются в самом конце, после всех невиртуальных
базовых классов и производного класса.
• Это связано с тем, что виртуальные базовые классы инициализируются первыми (при
конструировании), а освобождаются последними (при разрушении).
Пример:
cpp
class X { virtual ~X() { /* ... */ } };
class Y : virtual public X { ~Y() { /* ... */ } };
class Z : virtual public X { ~Z() { /* ... */ } };
class W : public Y, public Z { ~W() { /* ... */ } };
Порядок разрушения объекта W:
~W() — деструктор W.
~Y() и ~Z() — деструкторы невиртуальных базовых классов (Y и Z).
~X() — деструктор виртуального базового класса X.
33

34. 3.14. Множественное наследование. Деструкторы

4. Виртуальные деструкторы
Для корректной работы с указателями на базовые классы деструкторы должны быть
виртуальными:
cpp
class Base {
public:
virtual ~Base() { /* ... */ } // Виртуальный деструктор
};
class Derived : public Base {
public:
~Derived() { /* ... */ }
};
Если деструктор базового класса не виртуальный, при удалении через указатель на Base может
произойти утечка ресурсов или некорректное освобождение памяти.
34

35. 3.15. Множественное наследование. Деструкторы

5. Особенности работы деструкторов
Автоматическое вызов: деструкторы вызываются автоматически при выходе объекта из
области видимости или при явном удалении (delete).
Отсутствие аргументов: деструкторы не могут принимать параметры.
Не наследуются напрямую, но вызываются в цепочке разрушения.
Не могут быть const или static.
6. Проблемы и нюансы
Неоднозначность при множественном наследовании: если несколько базовых классов имеют
одноимённые виртуальные деструкторы, порядок вызова определён стандартом, но требует
внимательного анализа.
Исключения в деструкторах: если деструктор генерирует исключение, это критическая ошибка
— программа может аварийно завершиться. Рекомендуется избегать исключений в
деструкторах.
Порядок освобождения ресурсов: важно учитывать порядок освобождения зависимостей
(например, если один класс управляет памятью другого).
35

36. 3.16. Множественное наследование. Деструкторы

7. Пример с явным удалением
cpp
Base* obj = new Derived();
delete obj; // Вызов виртуального деструктора — сначала ~Derived(), затем ~Base()
Если ~Base() не виртуальный, будет вызван только деструктор Base, а ресурсы Derived не освободятся.
8. Рекомендации
Всегда делайте деструкторы базовых классов виртуальными, если предполагаете наследование.
Проверяйте порядок разрушения в сложных иерархиях.
Избегайте исключений в деструкторах.
Используйте инструменты отладки (например, printf в деструкторах) для отслеживания порядка вызова
при разработке.
Вывод
Деструкторы при множественном наследовании работают по чётким правилам: вызываются в обратном
порядке относительно конструкторов, с особым приоритетом для виртуальных базовых классов. Корректная
реализация виртуальных деструкторов критически важна для безопасного управления памятью в иерархиях
классов.
36

37. 3.17. Множественное наследование. Объявление, создание и разрушение объектов

1. Объявление классов с множественным наследованием
Множественное наследование в C++ позволяет производному классу наследовать свойства и методы от
нескольких базовых классов. Синтаксис объявления выглядит следующим образом:
cpp
class ПроизводныйКласс : спецификатор_доступа БазовыйКласс1,
спецификатор_доступа БазовыйКласс2,
...
спецификатор_доступа БазовыйКлассN {
// Тело класса
};
Пример:
cpp
class A { };
class B { };
class C : public A, public B { }; // C наследует от A и B
Важные ограничения:
одно и то же имя класса не может входить более одного раза в список баз при объявлении производного
класса;
один и тот же класс может участвовать в формировании нескольких (или всех) непосредственных
базовых классов данного производного класса (для непрямых базовых классов ограничений нет).
37

38. 3.18. Множественное наследование. Объявление, создание и разрушение объектов

2. Создание объектов (вызов конструкторов)
При создании объекта производного класса порядок вызова конструкторов определяется порядком
расположения базовых классов в списке наследования.
Порядок вызова конструкторов:
1) Сначала вызываются конструкторы базовых классов (в порядке их перечисления).
2) Затем вызывается конструктор производного класса.
Пример порядка вызова для class C : public A, public B:
1) Конструктор A.
2) Конструктор B.
3) Конструктор C.
Инициализация через список инициализации: Конструктор производного класса должен явно
инициализировать базовые классы в списке инициализации:
cpp
C::C() : A(/*аргументы для A*/), B(/*аргументы для B*/) {
// тело конструктора C
}
Особенности виртуального наследования: Если используется виртуальное наследование, виртуальные
базовые классы конструируются перед невиртуальными, вне зависимости от их расположения в списке
наследования.
38

39. 3.19. Множественное наследование. Объявление, создание и разрушение объектов

3. Разрушение объектов (вызов деструкторов). При уничтожении объекта порядок вызова деструкторов
обратный порядку вызова конструкторов. Порядок вызова деструкторов:
1) Сначала вызывается деструктор производного класса.
2) Затем деструкторы базовых классов (в порядке, обратном их перечислению в списке наследования).
Пример для class C : public A, public B:
1) Деструктор C.
2) Деструктор B.
3) Деструктор A.
Особенности для виртуальных базовых классов: Виртуальные базовые классы разрушаются в самом конце,
после всех невиртуальных базовых классов и производного класса.
Пример с виртуальным наследованием:
cpp
class X { virtual ~X() { } };
class Y : virtual public X { ~Y() { } };
class Z : virtual public X { ~Z() { } };
class W : public Y, public Z { ~W() { } };
Порядок разрушения объекта W:
1) ~W() — деструктор W.
2) ~Y() и ~Z() — деструкторы невиртуальных базовых классов.
3) ~X() — деструктор виртуального базового класса X.
39

40. 3.20. Множественное наследование. Объявление, создание и разрушение объектов

4. Важные аспекты
1. Виртуальные деструкторы: Для корректной работы с указателями на базовые классы деструкторы
базовых классов должны быть виртуальными. Иначе при удалении через указатель на базовый класс могут
возникнуть утечки ресурсов или некорректное освобождение памяти.
2. Неоднозначность доступа к членам базовых классов: При множественном наследовании может
возникнуть неоднозначность при доступе к членам базовых классов с одинаковыми именами. Для
разрешения неоднозначности нужно явно указывать класс:
cpp
MyD.B::x0 = 100; // Доступ к x0 в классе B
MyD.C::x0 = 100; // Доступ к x0 в классе C.
3. Управление памятью и ресурсами: Необходимо внимательно следить за порядком освобождения
ресурсов, особенно если один класс управляет памятью другого.
Итог:
•порядок вызова конструкторов определяет структуру создания объекта;
•порядок вызова деструкторов обеспечивает корректное освобождение ресурсов;
•виртуальные деструкторы критически важны для безопасного управления памятью;
•порядок наследования напрямую влияет на логику работы программы.
40

41. Рекомендуемые источники по теме лекции

• С++ базовый курс. Герберт Шилдт. Диалектика-Вильямс, 2018. —
624 с.
• C++ для начинающих. Стенли Липпман. Санкт-Петербург:
Издательство «Лань». 2020. 236 с.
• Язык программирования C++. Бьёрн Страуструп. БИНОМ.
Лаборатория знаний. 2019. - 1136 с.
• Язык программирования C++. Лекции и упражнения, 6-е изд.
Стивен Прата. Вильямс, 2012. — 1248 с.
41
English     Русский Rules