ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 13.12.2020
Просмотров: 4310
Скачиваний: 28
Часть II. Язык программирования С++
226
В любом случае
private
члены базового класса становятся
private
членами производного класса, причем доступ к этим чле-
нам закрыт в производном классе. Чтобы открыть этот доступ,
производный класс должен быть объявлен другом базового класса.
Если спецификатор доступа перед базовым классом опущен, то
по умолчанию для классов он устанавливается в
private
, а для
структур — в
public
.
В табл. 17.1 суммированы правила действия спецификатора дос-
тупа для членов базового класса.
Таблица 17.1.
Спецификаторы доступа для членов базового класса
Спецификатор
доступа
Доступ
в базовом классе
Доступ
в производном классе
public
public
protected
private
public
protected
private
protected
public
protected
private
protected
protected
private
private
public
protected
private
private
private
private
17.3. Конструкторы, деструкторы
и наследование
Конструкторы не наследуются. Если бы конструкторы наследо-
вались, даже при условии их переименования, то можно было бы
создать объект производного класса путем вызова конструктора
только базового класса. А это привело бы к ошибке. Хотя конст-
рукторы и не наследуются, но при создании нового объекта про-
изводного класса компилятор всегда вызывает конструктор по
Глава 17. Наследование классов
227
умолчанию базового класса. Чтобы изменить такую инициализа-
цию объекта производного класса, нужно в списке инициализа-
ции конструктора производного класса явно вызвать требуемый
конструктор базового класса.
В листинге 17.4 приведен явный и неявный вызов конструктора
базового класса.
Листинг 17.4. Явный вызов конструктора базового класса
#include <iostream>
using namespace std;
struct Base
{
int n;
Base(const int _n = 10): n(_n) {}
};
struct Derived: Base
{
int m;
Derived(const int _m = 10): m(_m) {}
Derived(const int _n, const int _m): Base(_n), m(_m) {}
};
int main()
{
Derived c;
cout << c.n << ' ' << c.m << endl; // 10 10
Derived d(5, 15);
cout << d.n << ' ' << d.m << endl; // 5 15
return 0;
}
При инициализации любого объекта конструкторы вызываются
в следующем порядке:
1.
Конструкторы базовых классов. Если таких классов несколь-
ко, то их конструкторы вызываются в порядке следования
этих классов в списке базовых классов, а не в порядке их по-
явления в списке инициализации.
Часть II. Язык программирования С++
228
2.
Конструкторы объектов-членов класса. Если таких объектов
несколько, то их конструкторы вызываются в порядке объяв-
ления этих объектов в классе, а не в порядке их появления
в списке инициализации.
3.
Конструктор производного класса.
Деструкторы не наследуются по той же причине, что и конструк-
торы. Однако при уничтожении объекта всегда вызываются дест-
рукторы базовых классов. Причем вызываются деструкторы в
порядке, обратном вызову конструкторов.
В листинге 17.5 приведен пример неявного вызова деструктора
базового класса.
Листинг 17.5. Неявный вызов деструктора базового класса
#include <iostream>
using namespace std;
class Base
{
public:
~Base() { cout << "Base class" << endl; }
};
class Derived: Base
{
public:
~Derived() { cout << "Derived class" << endl; }
};
int main()
{
{
Derived d;
}
// печатает: Derived class
// печатает: Base class
return 0;
}
Глава 17. Наследование классов
229
17.4. Наследование и присваивание
В языке программирования C++ базовый класс совместим по ти-
пу со всеми производными от него классами. Поэтому объекты
производных классов можно присваивать объекту базового клас-
са, но не наоборот.
В листинге 17.6 приведен пример присваивания объекту базового
класса объекта производного класса.
Листинг 17.6. Присваивание объекту базового класса объекта
производного класса
#include <iostream>
using namespace std;
class Base
{
int n;
public:
Base(int _n): n(_n) {}
int what() { return n; }
};
class Derived: public Base
{
int m;
public:
Derived(int _n, int _m): Base(_n), m(_m) {}
int what() { return m; }
};
int main()
{
Base b(10);
Derived d(20, 20);
cout << b.what() << endl; // печатает: 10
b = d;
cout << b.what() << endl; // печатает: 20
return 0;
}
Часть II. Язык программирования С++
230
Операторы присваивания не наследуются, т. к. при отсутствии
оператора присваивания в производном классе компилятор гене-
рирует для этого класса оператор присваивания по умолчанию,
который вызывает операторы присваивания для базовых классов
и выполняет тривиальное копирование членов-данных производ-
ного класса.
17.5. Виртуальные функции
Язык программирования C++ обеспечивает механизм для вызова
функции производного класса в случае, если доступ к объекту
производного класса осуществляется через указатель или ссылку
на базовый класс. Для этого в производном классе должна быть
определена функция, имя и сигнатура которой совпадают с име-
нем и сигнатурой функции базового класса, причем функция ба-
зового класса должна быть объявлена со спецификатором
virtual
. В этом случае говорят, что функция производного класса
замещает
(
overrides
) функцию базового класса.
В листинге 17.7 приведен пример вызова функции производного
класса через указатель на базовый класс.
Листинг 17.7. Вызов функции производного класса
через указатель на базовый класс
#include <iostream>
using namespace std;
class Base
{
public:
virtual int what() { return 10; }
};
class Derived: public Base
{
public:
virtual int what() { return 20; }
};