Глава №12. Итоговый тест

  Юрий  | 

    | 

  Обновл. 27 Июн 2019  | 

 1977

 ǀ   2 

Итак, наше путешествие в наследование и виртуальные функции в C++ подошло к концу. Пора закрепить пройденный материал.

Теория

C++ позволяет создавать указатели/ссылки родительского класса на объекты дочерних классов. Это полезно при работе с функциями или массивами, которые должны работать с объектами дочерних классов.

Без виртуальных функций указатели/ссылки родительского класса на объект дочернего класса будут иметь доступ только к членам родительского класса.

Виртуальная функция — это особый тип функции, которая, при её вызове, вызывает наиболее дочерний метод (переопределение), которое существует между родительским и дочерними классами. Чтобы считаться переопределением, метод дочернего класса должен иметь ту же сигнатуру и тип возврата, что и виртуальная функция родительского класса. Единственное исключение — ковариантный тип возврата, который позволяет переопределению возвращать указатель или ссылку на дочерний класс, если метод родительского класса возвращает указатель или ссылку на себя.

Модификатор override используется для обозначения метода переопределением.

Модификатор final запрещает переопределять виртуальную функцию или наследовать определённый класс.

Используя виртуальные функции, не забывайте добавлять в родительский класс виртуальный деструктор, чтобы в случае удаления указателя на родительский класс вызывался соответствующий деструктор.

Вы можете игнорировать вызов переопределений виртуальной функции, используя оператор разрешения области видимости, чтобы напрямую указать, какую функцию вы хотите вызвать. Например, parent.Parent::GetName().

Раннее связывание происходит, когда компилятор встречает прямой вызов функции. Компилятор или линкер могут напрямую обрабатывать прямые вызовы функций. Позднее связывание происходит при вызове указателя на функцию. В таких случаях невозможно знать наперёд, какая функция будет вызываться первой. Виртуальные функции используют позднее связывание и виртуальные таблицы для определения того, какую версию функции следует вызывать.

Относительные недостатки виртуальных функций:

   Вызов виртуальных функций занимает больше времени.

   Необходимость наличия виртуальной таблицы увеличивает размер каждого объекта класса, содержащего виртуальную функцию, на размер одного указателя.

Виртуальную функцию можно сделать чистой виртуальной/абстрактной функцией, добавив = 0 в конец прототипа виртуальной функции. Класс, содержащий чистую виртуальную функцию, называется абстрактным классом. Объекты абстрактного класса не могут быть созданы. Класс, который наследует чистые виртуальные функции, должен предоставить свои переопределения этих функций, или он также будет считаться абстрактным. Чистые виртуальные функции могут иметь тело (определение, записанное отдельно), но они по-прежнему считаются абстрактными функциями.

Интерфейс (или ещё «интерфейсный класс») — это класс без переменных-членов, все методы которого являются чистыми виртуальными функциями. Имена интерфейсов часто начинаются с I.

Виртуальный базовый класс — это родительский класс, объект которого является общим для использования всеми дочерними классами.

При присваивании объекта дочернего класса объекту родительского класса, в объект родительского класса копируется лишь родительская часть копируемого объекта, дочерняя часть копируемого объекта обрезается. Этот процесс называется обрезкой объектов.

Динамическое приведение используется для конвертации указателя родительского класса в указатель дочернего класса. Это называется понижающим приведением типа. Если конвертация прошла неудачно, то возвращается нулевой указатель.

Самый простой способ перегрузить оператор вывода << для классов с наследованием — записать перегрузку оператора << в родительском классе, а выполнение операции вывода делегировать виртуальному методу.

Тест


Задание №1

Каждая из следующих программ имеет какую-то ошибку. Ваша задача состоит в том, чтобы найти эту ошибку визуально (не запуская код). Предполагаемый вывод каждой программы:

Child

a)

Ответ 1.a)

Parent::getName() не является виртуальной функцией, поэтому p.getName() не вызовет Child::getName().

b)

Ответ 1.b)

Хотя Child::getName() является константным, Parent::getName() не является константным, поэтому Child::getName() не считается переопределением и, следовательно, не вызывается.

c)

Ответ 1.c)

Объект ch присваивается объекту p по значению (а не по ссылке), что приводит к обрезке объекта ch.

d)

Ответ 1.d)

Класс Parent был объявлен как final, поэтому класс Child не может наследовать класс Parent. Результат — ошибка компиляции.

e)

Ответ 1.e)

Child::getName() является абстрактной функцией, хоть и имеет тело (записанное отдельно), поэтому класс Child является абстрактным, а объекты абстрактного класса создавать нельзя.

f)

Ответ 1.f)

Эта программа выводит верный результат, но имеет другую проблему. В конце функции main() мы удаляем p, который является указателем класса Parent, но у нас нет виртуального деструктора в классе Parent. Следовательно, удаляется только часть Parent объекта класса ch, а часть Child объекта ch остаётся в виде утечки памяти.

Задание №2

a) Создайте абстрактный класс Shape. Этот класс должен иметь три метода:

   чистую виртуальную функцию print() с параметром типа std::ostream;

   перегрузку operator<<;

   пустой виртуальный деструктор.

Ответ 2.a)

b) Создайте два класса: Triangle и Circle, которые наследуют класс Shape.

   Triangle должен иметь 3 точки в качестве переменных-членов.

   Circle должен иметь одну центральную точку и целочисленный радиус в качестве переменных-членов.

Перегрузите функцию print(), чтобы следующий код:

Производил следующий результат:

Circle(Point(1, 2, 3), radius 7)
Triangle(Point(1, 2, 3), Point(4, 5, 6), Point(7, 8, 9))

Вот класс Point, который вы можете использовать:

Ответ 2.b)

c) Используя код из предыдущих заданий (классы Point, Shape, Circle и Triangle) завершите следующую программу:

Подсказка: Вам нужно добавить метод getRadius() в Circle и выполнить понижающее приведение Shape* в Circle*, чтобы получить доступ к этому методу.

Ответ 2.c)

Оценить статью:

Звёзд: 1Звёзд: 2Звёзд: 3Звёзд: 4Звёзд: 5 (19 оценок, среднее: 4,74 из 5)
Загрузка...

Комментариев: 2

  1. Аватар koh:

    1 — 7 глава :
    Хм… это очень интересно, я все понимаю. Бывают сложности, но все же, это не проблемно.
    8 — 10 глава.
    Мдэ. ну, впринципе, терпимо.
    10 — 11 глава.
    Я. Что — то. прочитал.

    Спасите

  2. Аватар kmish:

    Мой вариант. Я членами классов Triangle и Circle сделал ссылки на объекты Point, дабы избежать их копирования в объекты классов Triangle и Circle.

Добавить комментарий

Ваш E-mail не будет опубликован. Обязательные поля помечены *