ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 13.12.2020
Просмотров: 4315
Скачиваний: 28
Часть II. Язык программирования С++
236
class Derived: public Base
{
public:
virtual ~Derived() { cout << "~Derived" << endl; }
};
int main()
{
Base* b = new Derived;
delete b; // ~Derived ~Base
return 0;
}
17.9. Абстрактные классы
Виртуальная функция называется
чистой
(pure), если она опреде-
лена только как спецификатор интерфейса, т. е.
не имеет реали-
зации
. Чисто виртуальные функции имеют следующий прототип:
virtual тип имя_функции(параметры) = 0;
Предполагается, что чисто виртуальная функция определяется
в производных классах.
Класс, который содержит хотя бы одну чисто виртуальную функ-
цию, называется
абстрактным
(abstract)
классом
. Такой класс
может использоваться только в качестве базового класса для дру-
гих классов.
Так как у чисто виртуальных функций отсутствует реализация, то
невозможно создать объект, принадлежащий абстрактному клас-
су. Отсюда следует, что абстрактные классы также подчиняются
следующим требованиям:
абстрактный класс не может использоваться в качестве аргу-
мента или типа возвращаемого функцией значения;
абстрактный класс нельзя использовать в явном преобразова-
нии типов.
Можно определить указатель или ссылку на абстрактный
класс — это позволит использовать абстрактные классы для опи-
Глава 17. Наследование классов
237
сания интерфейсов базовых классов и работы с производными
классами через указатели или ссылки на базовый класс.
Если класс, производный от абстрактного класса, не определяет
все чисто виртуальные функции, то он также является абстракт-
ным классом.
Так как деструктор абстрактного класса может быть вызван ком-
пилятором при уничтожении объекта производного класса, то
деструктор абстрактного класса должен быть определен как вир-
туальная функция с пустым телом, а не как чисто виртуальная
функция.
В листинге 17.12 приведен пример использования ссылки на аб-
страктный класс в качестве параметра функции.
Листинг 17.12. Ссылка на абстрактный класс
#include <iostream>
using namespace std;
class Abstract
{
public:
virtual int what() = 0;
virtual ~Abstract() {}
};
class Concrete: public Abstract
{
public:
virtual int what() { return 10; }
virtual ~Concrete() {}
};
int foo(Abstract& a)
{
return a.what();
}
int main()
{
Часть II. Язык программирования С++
238
Concrete c;
cout << foo(c) << endl; // 10
return 0;
}
17.10. Множественное наследование
В языке программирования C++ разрешается наследовать произ-
водный класс от нескольких базовых классов. Такое наследова-
ние называется
множественным
(multiple). В иерархии с множе-
ственным наследованием производный класс может косвенно
наследовать несколько экземпляров базового класса. В этом слу-
чае для доступа к повторяющимся членам базового класса ис-
пользуется оператор разрешения области видимости
::
с имена-
ми базовых классов в качестве квалификаторов.
В листинге 17.13 приведен пример множественного наследования
классов.
Листинг 17.13. Множественное наследование классов
#include <iostream>
using namespace std;
struct Base
{
int n;
int what() { return n; }
};
struct Derived_1: Base {};
struct Derived_2: Base {};
struct Derived: Derived_1, Derived_2 {};
int main()
{
Derived d;
d.Derived_1::n = 10;
d.Derived_2::n = 20;
Глава 17. Наследование классов
239
cout << d.Derived_1::what() << endl; // печатает: 10
cout << d.Derived_2::what() << endl; // печатает: 20
return 0;
}
17.11. Виртуальное наследование
Для того чтобы исключить передачу нескольких экземпляров ба-
зового класса по иерархии при множественном наследовании,
непосредственные потомки этого базового класса должны объ-
явить его виртуальным при помощи ключевого слова
virtual
в списке базовых классов. Такое наследование базового класса
называется
виртуальным
, а сам базовый класс называется
вир-
туальным базовым классом
.
В листинге 17.14 приведен пример наследования членов вирту-
ального базового класса.
Листинг 17.14. Наследование членов виртуального базового
класса
#include <iostream>
using namespace std;
struct Base
{
int n;
int what() { return n; }
};
struct Derived_1: virtual Base {};
struct Derived_2: virtual Base {};
struct Derived: Derived_1, Derived_2 {};
int main()
{
Derived d;
d.n = 10;
cout << d.what() << endl; // печатает: 10
Часть II. Язык программирования С++
240
return 0;
}
При виртуальном наследовании необходимо, чтобы конструктор
виртуального базового класса вызывался только один раз. Поэто-
му при создании объекта производного класса сначала вызыва-
ются конструкторы всех базовых виртуальных классов, а затем
конструкторы остальных базовых классов в порядке их следова-
ния в иерархии.