Иногда класс должен переопределить некоторые, но не все функции в наборе перегруженных. В таких случаях было бы весьма утомительно переопределять каждую версию базового класса, чтобы переопределить только те, которые должен специализировать класс.
Вместо переопределения каждой версии базового класса, которую он унаследовал, производный класс может предоставить объявление using
(см. раздел 15.5) для перегруженного члена. Объявление using
определяет только имя; оно не может определить список параметров. Таким образом, объявление using
для функции-члена базового класса добавляет все перегруженные экземпляры этой функции в область видимости производного класса. Перенеся все имена в свою область видимости, производный класс должен определить только те функции, которые действительно зависят от его типа.
Обычные правила объявления using
в классе относятся и к именам перегруженных функций (см. раздел 15.5); каждый перегруженный экземпляр функции в базовом классе должен быть доступен в производном классе. Доступ к перегруженным версиям, которые в противном случае не переопределяются производным классом, будет возможен в точке объявления using
.
Упражнение 15.23. Предположим, что класс D1
намеревается переопределить свою унаследованную функцию fcn()
. Как исправить этот класс? Предположим, что класс исправлен так, что функция fcn()
соответствует определению в классе Base
. Как бы распознавались вызовы в этом разделе?
15.7. Конструкторы и функции управления копированием
Подобно любому другому классу, класс в иерархии наследования контролирует происходящее при создании, копировании, перемещении, присвоении или удалении объектов его типа. Как и у любого другого класса, если класс (базовый или производный) сам не определяет одну из функций управления копированием, ее синтезирует компилятор. Кроме того, как обычно, синтезируемая версия любой из этих функций-членов может быть удаленной функцией.
Основное воздействие, которое наследование оказывает на управление копированием для базового класса, заключается в том, что базовый класс обычно должен определять виртуальный деструктор (см. раздел 15.2.1). Деструктор должен быть виртуальной функцией, чтобы обеспечить объектам в иерархии наследования возможность динамического создания.
Помните, что деструктор выполняется при удалении указателя на динамически созданный объект (см. раздел 13.1.3). Если это указатель на тип в иерархии наследования, вполне возможно, что статический тип указателя может отличаться от динамического типа удаляемого объекта (см. раздел 15.2.2). Например, при удалении указателя типа Quote*
может оказаться, что он указывал на объект класса Bulk_quote
. Если он указывает на объект типа Bulk_quote
, компилятор должен знать, что следует выполнить деструктор именно класса Bulk_quote
. Подобно любой другой функции, чтобы был выполнен надлежащий деструктор, в базовом классе его следует определить как виртуальную функцию:
class Quote {
public:
//
//
virtual ~Quote() = default; //
//
};
Подобно любой другой виртуальной функции, виртуальный характер деструктора наследуется. Таким образом, у классов, производных от класса Quote, окажутся виртуальные деструкторы, будь то синтезируемый деструктор или собственный. Пока деструктор базового класса остается виртуальной функцией, при удалении указателя на базовый класс будет выполнен соответствующий деструктор:
Quote *itemP = new Quote; //
delete itemP; //
itemP = new Bulk_quote; //
delete itemP; //
delete
для указателя на базовый класс, который указывает на объект производного класса, приведет к непредсказуемым последствиям, если деструктор базового класса не будет виртуальным.