Наследование Полиморфизм ВЫЗОВ КОНСТРУКТОРОВ И ДЕСТРУКТОРОВ ПРИ НАСЛЕДОВАНИИ.

Презентация:



Advertisements
Похожие презентации
Что такое композиция? Композиция (агрегирование, включение) – простейший механизм для создания нового класса путем объединения нескольких объектов существующих.
Advertisements

Прикладное программирование кафедра прикладной и компьютерной оптики Наследование.
Лекция 4. Введение в С++ Наследование, множественное наследование. Конструкторы, деструкторы. Виртуальные функции.
НГТУ, каф. ВТ Наследование в С++ Макаревич Л. Г.НГТУ, каф. ВТ Наследование в С++ Макаревич Л. Г.
Основы информатики Классы Заикин Олег Сергеевич zaikin.all24.org
Производные классы Определение класса посредством добавления возможностей к уже имеющемуся классу без перепрограммирования или перекомпиляции самого класса.
НаследованиеНаследование2 class Point { double x; double y; Color color; }; class Radius { Point center; double radius; };
Наследование Наследование – это отношение является между классами. class Person { string first_name; int birth_year;... } class Student : Person { float.
Лекция 8. Введение в ООП. Часть 1 Красс Александр СПбГУ ИТМО, 2008.
Полиморфизм Полиморфизм (polymorphism) - последний из трех "китов", на которых держится объектно-ориентированное программирование Слово это можно перевести.
С++, начала ООП Семинар 3 Рябова Анна Сергеевна
Практическое занятие 6. Функции. Большинство языков программирования используют понятия функции и процедуры. C++ формально не поддерживает понятие процедуры,
Полиморфизм. Полиморфизм – это свойство системы использовать объекты с одинаковым интерфейсом без информации о типе и внутренней структуре объекта.
Преобразования типов В языке C/C++ имеется несколько операций преобразования типов. Они используются в случае, если переменная одного типа должна рассматриваться.
Прикладное программирование кафедра прикладной и компьютерной оптики Абстрактные типы данных.
Лекция 10. Введение в ООП. Часть 3 Красс Александр СПбГУ ИТМО, 2008.
Лекция 10 ОбъектыЛекция 10 ОбъектыООП Инкапсуляция Возможность совместного хранения данных и кода для их обработки Наследование Возможность расширять существующие.
Объектно-ориентированное программирование Парадигма программирования, основанная на представлении предметной области в виде взаимосвязанных абстрактных.
Что такое обобщенное программирование? Парадигма программирования, заключающаяся в написании алгоритмов, которые можно применять к различным типам данных.
©Павловская Т.А. Язык С++ Курс «С++. Программирование на языке высокого уровня» Павловская Т.А.
Транксрипт:

Наследование Полиморфизм ВЫЗОВ КОНСТРУКТОРОВ И ДЕСТРУКТОРОВ ПРИ НАСЛЕДОВАНИИ

Сравнение типов наследования

Порядок вызова конструкторов В С++ при конструировании экземпляра-наследника всегда происходит предварительный вызов конструктора базового класса В С++ вызов конструктора базового класса происходит до инициализации полей класса наследника Конструктор класса-наследника может явно передать конструктору базового класса необходимые параметры при помощи списка инициализации Если вызов конструктора родительского класса не указан в списке инициализации, компилятор пытается вызвать конструктор по умолчанию класса-родителя

Пример class CEmployee {public: std::string GetName()const {return m_name;} protected: CEmployee(std::string const& name) : m_name(name) {std :: cout

Пример class CEmployee{ public: string GetName()const {return m_name;} protected: CEmployee(string const& name): m_name(name) {cout

Порядок вызова деструкторов В С++ порядок вызова деструкторов ВСЕГДА обратен порядку вызова конструкторов Сначала вызывается деструктор класса- наследника, затем деструктор базового класса и т.д. вверх по иерархии классов

Пример class CTable{ public: CTable(string const& dbFileName) { m_tableFile.Open(dbFileName); cout

ПЕРЕГРУЗКА МЕТОДОВ В КЛАССЕ- НАСЛЕДНИКЕ

Перегрузка методов в классе-наследнике В С++ метод производного класса замещает собой ВСЕ МЕТОДЫ родительского класса С ТЕМ ЖЕ ИМЕНЕМ Количество и типы аргументов значения не имеют Для вызова метода родительского класса из метода класса наследника используется метод Base::

Пример class CBase{ public: void Print ( ) { cout

Виртуальные функции

Задача – иерархия геометрических фигур Рассмотрим следующую иерархию геометрических фигур: CShape – базовый класс «фигура» CCircle – класс, моделирующий окружность CRectangle – класс, моделирующий прямоугольник Каждая фигура обладает следующими свойствами: Имя: «Shape», «Circle», «Rectangle» Площадь фигуры

Решение без виртуальных функций class CShape{ public: string GetType() const {return Shape;} double GetArea() const {return 0;} }; class CRectangle : public CShape {public: CRectangle (double width, double height) : m_width(width), m_height(height) {} string GetType () const {return Rectangle;} double GetArea()const {return m_width*m_height;} private: double m_height; double m_width; }; class CCircle : public CShape {public: CCircle (double radius) : m_radius(radius) { } string GetType ( ) const {return Circle;} double GetArea( ) const {return 3.14*m_radius*m_radius;} private: double m_radius;};

Так, вроде, все работает int main ( ) { CCircle circle(10); CRectangle rectangle(20,10); cout

А вот так - нет void PrintShapeArea (CShape const& shape) { cout

Проблема Проблема в том, что в данной ситуации, при выборе вызываемых методов компилятор руководствуется типом ссылки или указателя В нашем случае происходит вызов метода класса CShape, так как функция PrintShapeArea принимает ссылку данного типа Методы, при вызове которых необходимо руководствоваться типом объекта, должны быть объявлены виртуальными

Виртуальные методы Метод класса может быть объявлен виртуальным, если допускается его альтернативная реализация в порожденном классе При вызове виртуальной функции через указатель или ссылку на объект базового класса будет вызвана реализация данной функции, специфичная для фактического типа объекта Виртуальные функции обозначаются в объявлении класса при помощи ключевого слова virtual Виртуальные функции позволяют использовать полиморфизм Полиморфизм позволяет осуществлять работу с разными реализациями через один и тат же интерфейс

Решение с виртуальными функциями class CShape{ public: virtual string GetType() const {return Shape;} virtual double GetArea() const {return 0;} }; class CRectangle : public CShape {public: CRectangle (double width, double height) : m_width(width), m_height(height) {} virtual string GetType () const {return Rectangle;} virtual double GetArea()const {return m_width*m_height;} private: double m_height; double m_width; }; class CCircle : public CShape {public: CCircle (double radius) : m_radius(radius) { } virtual string GetType ( ) const {return Circle;} virtual double GetArea( ) const {return 3.14*m_radius*m_radius;} private: double m_radius;};

Теперь работает правильно void PrintShapeArea (CShape const& shape) { cout

Особенности реализации виртуальных функций в С++ В С++ функции, объявленные в базовом классе виртуальными, остаются виртуальными в классе-потомке Использовать слово virtual в классах-наследниках не обязательно (хотя и желательно) В С++ виртуальные функции не являются виртуальными, если они вызваны в конструкторе или деструкторе данного класса Такое поведение специфично для механизма инициализации и разрушения объектов в С++; в других языках программирования может быть и по- другому

class CShape{ public: virtual string GetType() const {return Shape;} virtual double GetArea() const {return 0;} }; class CRectangle : public CShape {public: CRectangle (double width, double height) : m_width(width), m_height(height) {} virtual string GetType () const {return Rectangle;} virtual double GetArea()const {return m_width*m_height;} private: double m_height; double m_width; }; class CCircle : public CShape {public: CCircle (double radius) : m_radius(radius) { } virtual string GetType ( ) const {return Circle;} virtual double GetArea( ) const {return 3.14*m_radius*m_radius;} private: double m_radius;};

Виртуальный деструктор Деструктор класса, имеющего наследников, всегда должен явно объявляться виртуальным Это обеспечивает корректный вызов деструктора нужного класса при вызове оператора delete с указателем на базовый класс Деструктор, не объявленный явно виртуальным, а также автоматически сгенерированный деструктор является не виртуальным Классы без виртуальных деструкторов не предназначены для расширения Классы стандартных коллекций STL (строки, векторы) не имеют виртуальных деструкторов, поэтому наследоваться от них нельзя

Проблемы при использовании невиртуального деструктора class CBase{ public: CBase ( ) : m_pBaseData (new char [100]) { cout

OUTPUT: Base class data were created Derived class data were created Derived class data were deleted Base class data were deleted Base class data were created Derived class data were created Derived class data were deleted Base class data were deleted Base class data were created Derived class data were created Base class data were deleted Проблемы при использовании невиртуального деструктора

Исправляем проблему, объявив деструктор виртуальным class CBase{ public: CBase ( ) : m_pBaseData (new char [100]) { cout

OUTPUT: Base class data were created Derived class data were created Derived class data were deleted Base class data were deleted Base class data were created Derived class data were created Derived class data were deleted Base class data were deleted Base class data were created Derived class data were created Derived class data were deleted Base class data were deleted Исправляем проблему, объявив деструктор виртуальным

ИТОГИ Всегда используем виртуалбный деструктор: В базовых классах В классах, от которых возможно наследование в будущем Например, в классах с виртуальными методами Не используем виртуальные деструкторы: В классах, от которых не планируется создавать производные классы в будущем Также возможно в базовом классе объявить защищенный невиртуальный деструктор Объекты данного напрямую удалить невозможно – только через указатель на класс-наследник Данный деструктор будет доступен классам- наследникам