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

  Юрий Ворон  | 

    | 

  Обновлено 4 Ноя 2018  | 

 333

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

Теория

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

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

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

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

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

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

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

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



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

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

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

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

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

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

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

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

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

Тест

Задание №1

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

Child

1a)

Ответ 1a)

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

1b)

Ответ 1b)

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

1c)

Ответ 1c)

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

1d)

Ответ 1d)

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

1e)

Ответ 1e)

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

1f)

Ответ 1f)

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

Задание №2

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

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

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

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

Ответ 2a)

2b) Создайте два класса: 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, который вы можете использовать:

Ответ 2b)

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

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

Ответ 2c)

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

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

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

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

ВОЛШЕБНАЯ ТАБЛЕТКА ПО С++