Лекция _5_Наследование.ppt
- Количество слайдов: 58
Наследование классов и виртуальные функции
1. Механизм наследования. Простое наследование Наследование позволяет одним объектам получать свойства других объектов. Класс-наследник ( в С++ это называется производный класс ) получает свойства родительского ( базового ) класса. Кроме этого, производный класс может добавлять к этим свойствам свои собственные.
Наследуются или не наследуются определённые поля и методы, определяется с помощью механизма наследования : public, private, protected. Механизм наследования private protected public Ключ доступа в базовом классе private protected public Доступ в производном классе нет private Нет protected public
При описании класса в его заголовке перечисляются все классы, являющиеся для него базовыми и может указываться механизм наследования (по умолчанию private): class имя: [private|protected|public]базовый_класс { }; тело_класса Например: class A {…}; class B {…}; class C {…}; class D: A, protected B, public C {…};
Простым называется наследование, при котором производный класс имеет одного родителя.
Диаграмма классов A -х: int + y: int B + z: int +show(): void
Поле x не наследуется (ключ доступа private), поле y наследуется. Внесём в программный код изменения: поле х объявим как protected, добавим метод set для установки значения поля х, изменим метод show.
Изменим механизм наследования, сделав его private. Тогда все поля и методы класса В будут закрытыми. Восстановим уровень доступа к элементам y и set:
Но процедура восстановления уровней доступа не вписывается в концепцию инкапсуляции. Поэтому лучше воспользоваться переопределением методов или виртуальной функцией.
2. Переопределение методов и виртуальные функции Рассмотрим идею полиморфизма на примере: У класса А есть поле x, методы set() и get(). Путём public-наследования создаётся класс B, в котором содержится дополнительное поле y. Логично, чтобы set() и get() оперировали не только полем x, но и y. Т. е. методы set() и get() в производном классе надо переопределить. Тогда в базовом классе эти методы надо определить как виртуальные (virtual).
Разница между переопределением и перегрузкой: при перегрузке меняется прототип метода, при переопределении – нет.
Если бы методы set() и get() не были бы объявлены как виртуальные, то вызывались бы всегда методы базового класса. Инструкция virtual используется только в классе A – виртуальность метода наследуема. Поэтому в дальнейшем при переопределении метода в классах, производных от В, метод останется виртуальным.
3. Многоуровневое наследование При многоуровневом наследовании производный класс является, в свою очередь, базовым для другого класса и т. д. Образуется иерархическая структура. Пример:
Результаты работы программы: В главной функции программы создаются объекты всех четырёх классов, из каждого объекта вызывается метод set() и метод get(). При вызове из объекта класса A вызываются методы этого класса. При вызове из объекта класса B метод set() вызывается «родной» , а метод get() – из класса A. При вызове из объекта класса C метод set() вызывается из класса B, а метод get() – «родной» . При вызове из объекта класса D метод set() вызывается из класса B, а метод get() – из класса С.
Диаграмма классов A D +k: int +n: int +set(n: int): void +get(): void B +set(n: int): void +get(): void C +m: int +set(n: int): void +get(): void +set(n: int): void
4. Многократное наследование В С++ класс может создаваться на основе сразу нескольких классов (многократное наследование). При многократном наследовании указывают через запятую базовые классы и механизм наследования для каждого класса. Пример многократного наследования:
При многократном наследовании могут возникать проблемы с неоднозначностью наследования членов базового класса. Пусть, например, наследуемые поля классов А и В имеют одинаковые названия – n. При обращении к таким полям необходимо явно указывать, из какого класса они наследованы. Если obj является объектом класса C, то к полю от класса А можно обратиться, как obj. A: : n.
В более сложных случаях можно использовать виртуальное наследование базового класса, при котором совпадающие поля не дублируются. Пример виртуального наследования:
Поскольку классами B и С наследование класса А выполняется виртуально, то дублирования поля n в классе D нет.
5. Конструкторы и деструкторы при наследовании В производном классе надо предусмотреть механизм передачи аргументов конструктору базового класса. Этот механизм таков: при определении конструктора производного класса после имени конструктора указывается имя конструктора базового класса со списком аргументов.
Синтаксис объявления конструктора производного класса: Имя_констр(аргументы): Имя_констр1(аргументы), Имя_констр2(аргументы), . . . { // Код конструктора производного класса } Пример определения конструктора при наследовании:
В производном классе Derivative надо предусмотреть механизм передачи аргументов конструктору базового класса. Причина в способе создания объектов производных классов: сначала вызывается конструктор базового класса, а затем – производного. Если базовых классов несколько, то их конструкторы вызываются в порядке наследования классов (очерёдность в списке наследуемых классов слева направо).
В конструкторе Derivative предусмотрена передача двух аргументов: один передаётся конструктору базового класса, а второй – конструктору производного класса. Далее приведён пример определения конструктора производного класса при многократном наследовании:
В результате выполнения программы получим:
Конструкторы вызываются в такой последовательности: конструктор класса Base 1, конструктор класса Base 2, конструктор класса Derivative. Деструкторы вызываются в обратной последовательности: деструктор Derivative, деструктор Base 2, деструктор Base 1.
6. Чисто виртуальные методы и абстрактные классы Чисто виртуальной функцией называется такая виртуальная функция, которая не имеет определения в базовом классе. Синтаксис объявления чисто виртуальной функции в базовом классе: virtual тип_результата имя_функции(аргументы)= 0; Класс, содержащий хотя бы одну чисто виртуальную функцию, называется абстрактным.
Абстрактные классы служат основой для создания более сложных производных классов. Использование чисто виртуальных функций позволяет избежать ошибок, связаныых с переопределением виртуальных функций в производных классах. Далее приводится пример использования чисто виртуальной функции.
Создаётся абстрактный класс Figure c полем R (линейный размер геометрической фигуры), конструктором и чисто виртуальной функцией area() для вычисления площади фигуры. На основе этого базового класса создаются 3 производных класса, где метод area() переопределяется.
Диаграмма классов Figure +R: double +Figure(R: double) +area(): virtual double Circle Square Triangle +area(): double
7. Примеры решения задач Все те вопросы, которые были поставлены, мы их все соберём в одно место. В. С. Черномырдин. Задача 1 Создать класс для реализации алгебраической формы комплексного числа. Создать производный класс, который использует тригонометрическое представление комплексного числа. Напомним: алгебраическая форма : z = x + i y, Тригонометрическая форма : z = r ∙exp( i φ). x = r ∙ cos φ ; y = r ∙ sin φ ; tg φ = y / x
Результат выполнения программы:
Базовый класс Compl. Alg: для работы с полями x и y используются виртуальные методы set() и show(). Конструктор без аргументов создаёт нулевое комплексное число, а конструктор с аргументами позволяет задать x и y. Производный класс Сompl : добавляются модуль r и угол phi. Методы set() и show() переопределяются. В каждом из этих методов сначала вызывается старая версия метода из базового класса, а затем метод расширяется для работы с тригонометрической формой записи.
Задача 2 Создать класс Polynom 1 для реализации полиномов степени ≤ n. Для реализации вычисления произведения двух полиномов степени ≤ n создать производный класс Polynom 2 (для полиномов степени ≤ 2 n).
Если полином умножается на , то получим полином: Для записи коэффициентов для x степени больше n в производном классе определяется массив b. Путём переопределения операторной функции [ ] выполняется индексация элементов массива.
Оператор () перегружается в базовом классе для вычисления значения полинома, а в производном классе этот оператор переопределяется с учётом расширенного набора коэффициентов. Произведение полиномов вычисляется с помощью внешней операторной функции *. При выполнении умножения P 3=P 1*P 2 коэффициенты старших слагаемых степени от n+1 до 2 n теряются. Это можно применить для вычисления рядов Тейлора для различных функций.