- •1. Объектно-ориентированный подход в программировании.
- •1.1 Инкапсуляция
- •1.2 Наследование
- •1.3 Полиморфизм
- •2.1 Перегружаемые (overload) функции
- •2.2 Перегружаемые (overload) операторы
- •2.3 Объектные типы данных: структуры, объединения, классы (обзор)
- •2.4. Конструкторы и деструкторы
- •2.8 Встраиваемые функции
- •2.7 Производные классы
- •2.9 Присваивание объектов
- •2.10 Передача в функции и возвращение объекта
- •2.11 Конструктор копирования
- •2.12 Указатели и ссылки на объекты
- •3.1. Модификаторы наследования
- •3.2 Конструкторы при наследовании
- •3.3 Деструкторы при наследовании
- •3.4 Совместимость типов
- •4. Виртуальные функции.
- •4.1. Раннее и позднее связывание
- •4.2 Полиморфизм и виртуальные методы
- •4.3. Использование указателей на базовые классы при адресации объектов произвольных классов.
- •4.4 Абстрактный класс
- •5. Дружественные функции.
- •5.1 “Дружественные” (friend) функции
- •6. Шаблоны функций и классов
- •6.1 Шаблоны функций
- •6.2 Шаблоны классов
3.3 Деструкторы при наследовании
Базовый класс, производный класс или и тот, и другой могут иметь конструкторы и/или деструкторы. Если и у базового, и у производного классов есть конструкторы и деструкторы, то конструкторы выполняются в порядке наследования, а деструкторы - в обратном порядке. То есть если А - базовый класс, В - производный из А, а С - производный из В (А-В-С ), то при создании объекта класса С вызов конструкторов будет иметь следующий порядок: конструктор А - конструктор В - конструктор С . Вызов деструкторов при разрушении этого объекта произойдет в обратном порядке: деструктор С - деструктор В - деструктор А.
Понять закономерность такого порядка несложно, поскольку базовый класс «не знает» о существовании производного класса, любая инициализация выполняется в нем независимо от производного класса, и, возможно, становится основой для инициализации, выполняемой в производном классе. С другой стороны, поскольку базовый класс лежит в основе производного, вызов деструктора базового класса раньше деструктора производного класса привел бы к преждевременному разрушению производного класса.
3.4 Совместимость типов
Наследование предъявляет свои требования к правилам совместимости типов.В добавление ко всему прочему, порожденный тип наследует совместимость со всеми типами предка. Эта расширенная совместимость имеет три формы:
• между экземплярами объектов;
• между указателями объектов;
• между формальными и фактическими параметрами.
Однако во всех трех формах необходимо запомнить, что совместимость типов распространяется от потомка к предку. Другими словами, порожденные классы можно свободно использовать вместо классов предка, но не наоборот.
Например,
Point APoint, *ptrPoint;
Circle ACircle, *ptrCircle;
При наличии этих объявлений следующие присваивания являются законными.
APoint = ACircle;
Обратные присваивания неверны.
Родительскому объекту можно присваивать объект любого порожденного им класса.
Чтобы было проще запомнить путь совместимости типов, давайте рассуждать таким образом
1) источник должен полностью заполнять объект назначения;
2) порожденные типы содержат все, что содержат родительские типы, посредством наследования. Следовательно, порожденный тип будет иметь или тот же размер, или (обычно) размер больший, чем его предок, но никогда не меньший размер.
Присваивание родительского объекта порожденному может оставить некоторые поля потомка неопределенным и после присваивания, что весьма опасно.
В предложениях присваивания будут копироваться только те поля, которые являются общими для обоих типов, то есть только поля предка.
Совместимость типов также действует между указателями на классы. Указатели на потомков можно присваивать указателям на предков по тем же общим правилам, как и для экземпляров объекта.
Формальный параметр объекта заданного класса может использовать в качестве фактического параметра объект такого же класса или любого порожденного класса. Например,
void Proc (Point param)
Тогда фактические параметры могут иметь тип Point, Circle и любой другой порожденный от них тип.
