
- •Структуры
- •Массив структур
- •Указатели на структуру
- •Битовые поля
- •Объединения
- •Перечисления
- •Переименование типов
- •Открытие файла
- •Функции работы с файлами
- •Форматный ввод/вывод
- •Ввод/вывод строк. Функции позиционирования
- •Чтение из двоичного файла и запись в него
- •Свойства ООП
- •1.1 Свойства ООП
- •Понятие класса в С++
- •1.2 Понятие класса С++
- •Создание класса
- •1.3 Создание класса
- •Члены класса
- •1.4 Члены класса
- •Инкапсуляция
- •1.5. Инкапсуляция
- •1.6 Функции-члены класса
- •Реализация понятия «дата» при помощи структуры
- •1.7 Реализация понятия «дата» при помощи структуры
- •Реализация понятия «дата» при помощи класса
- •1.8 Реализация понятия «дата» при помощи класса
- •Определение и вызов функций–членов класса
- •Выводы. Примеры
- •1.9 Выводы
- •1.10 Пример создания класса
- •Конструкторы
- •2.1 Конструкторы
- •2.2 Пример класса без конструктора
- •2.3 Пример класса с конструктором
- •2.4. Конструктор с параметрами по умолчанию
- •2.5. Деструкторы
- •2.6. Пример класса с конструктором и деструктором
- •2.7. Подставляемые или inline функции.
- •2.8. Подставляемые функции-члены.
- •2.9. Указатель this
- •2.10. Использование указателя this
- •Дружественные функции
- •3.1 Дружественные функции
- •3.2 Пример дружественной функции
- •3.3 Дружественные классы
- •3.4. Уточнение имени элемента.
- •3.5. Определение переменных непосредственно перед использованием
- •3.6. Статические элементы класса.
- •2.7. Пример
- •Перегруженные функции
- •4. Перегрузка функций и операций
- •4.1 Полиморфизм
- •4.2 Перегруженные функции
- •4.3 Перегрузка конструктора
- •4.4 Выбор экземпляра функции
- •4.5 Перегрузка стандартных операций
- •4.6 Перегрузка операций сложения и присваивания
- •4.7 Перегрузка операций ++ и --
- •4.8 Дружественные функции-операции
- •4.9 Перегрузка операции индексации []
- •4.10 Аргументы по умолчанию
- •5. Работа с объектами
- •5.1 Ссылки в С++
- •5.2 Передача объектов, как аргументов функции
- •5.3 Массивы объектов
- •5.4 Указатель на объект
- •5.5 Динамическое выделение памяти. Операторы new, delete.
- •5.6 Локальные классы
- •5.7 Вложенные классы
- •5.8 Имена заголовочных файлов в С++
- •5.9 Строковые переменные в С++.
- •6. Наследование
- •6.1 Понятие наследования
- •6.2 Базовые и производные классы
- •6.3 Одиночное и множественное наследование
- •6.4 Управление доступом производных классов
- •6.5 Пример
- •6.6 Конструкторы с параметрами при наследовании
- •6.7 Конструкторы при множественном наследовании
- •7. Виртуальные функции. абстрактные классы
- •7.1 Указатели на производные типы
- •7.2 Виртуальные функции
- •7.3 Пример
- •7.4 Чистые виртуальные функции и абстрактные типы
- •7.5 Виртуальные базовые классы
- •7.6 Шаблоны
- •7.7 Шаблоны функций
- •7.8 Пример
- •7.9 Использование шаблонов с двумя типами параметров
- •7.10 Шаблоны классов

6. Наследование
6.1 Понятие наследования
Наследование – это одна из важных черт языков объектно-ориентированного программирования. Классы используются для моделирования концепций реального и программного мира. Однако ни одна концепция не существует в изоляции. Она сосуществует с родственными концепциями.
Например, концепции круга и треугольника связаны тем, что и тот, и другой являются фигурами. Концепция фигуры является общей для них. Поэтому мы должны явно определить, что классы Circle и Triangle имеют общий базовый класс Shape. Представление понятий «круг» и «треугольник» в программе без введения понятия «фигура» означало бы потерю чего-то существенного.
Отношение наследования между классами может быть представлено в графическом виде.
Понятие производного класса и связанные с ним механизмы языка предназначены для выражения иерархических отношений, т.е. для отражения общности классов.
Наследованием реализуется возможностью объединять один класс другим во время объявления второго класса. Механизм наследования позволяет определять новые классы, на основе уже имеющихся.
6.2 Базовые и производные классы
Класс, на основе которого создаётся новый класс, называется базовым (родительским классом), а новый – производным (или наследником).
Производные классы наследуют свойства базовых классов, включая данные-члены и функции-члены. Кроме того, в производном классе могут быть объявлены дополнительные данные-члены и функции-члены.
Объявление производного класса имеет следующий синтаксис:
class <имя производного класса> : <спецификатор доступа> <имя базового класса> [, <спецификатор доступа> <имя базового класса> ]
{ ... };
Если члены базового класса не переопределены в производном классе, они обозначаются и трактуются так же, как и члены производного класса. Говорят, что члены базового класса наследуются производным классом. Операция разрешения области видимости :: может употребляться для явной ссылки на член базового класса. Это обеспечивает доступ к имени, которое переопределено в производном классе.
Пример: class Base
{ public:
int a, b;
};
class Derived : public Base
{ public: |
|
|
int b, c; |
|
|
}; |
|
|
Derived d; |
|
|
d.a |
= 1; |
// Инициализация a, унаследованного из класса Base |
d.Base::b = 2; |
// Инициализация b из класса Base |
|
d.b |
= 3; |
// Инициализация b, объявленного в классе Derived |
d.c |
= 4; |
|
Base *bp = &d; |
// Преобразуем указатель на класс Derived в указатель на класс Base |
6.3 Одиночное и множественное наследование
Производный класс может сам, в свою очередь, служить базовым классом.
Существует возможность одиночного и множественного наследования. При одиночном наследовании базовым является один класс, а при множественном – базовых классов может быть несколько.
class A {
public: void f(); };
class B : public A { }; // Класс B является непосредственным базовым классом для класса
C
class C : public B // класс A – косвенным базовым классом для класса C
{ public: void f();
void ff();
};
В записи <имя класса>::<имя> имя класса определяет класс, в котором начинается поиск имени.
void C::ff()
{ f(); |
// Вызов функции f() из класса C |
A::f(); // Вызов функции f() из класса A
B::f(); // Снова вызов функции f() из класса A, т.к. в классе В функция f() не определена
}
Непосредственно базовым классом называется такой класс, который входит в список базовых классов при определении класса. Любой производный класс может стать базовым для других создаваемых классов, таким образом реализуется иерархия классов и объектов.
6.4 Управление доступом производных классов
При наследовании важную роль играет статус доступа к компонентам класса. В иерархии классов существуют следующие соглашения о правах доступа к компонентам класса:
–собственные private элементы доступны только внутри класса, где они определены.
–защищённые protected элементы доступны внутри класса и во всех производных классах.
–общедоступные (открытые) public компоненты класса видимы из любой точки программы.
Таким образом для объекта, который обменивается сообщениями с другими объектами и обрабатывает их, доступными являются общедоступные компоненты всех объектов программы, защищённые данные и функции, являющиеся представителями базовых классов, и собственные компоненты объекта.
class X {
protected:
int i; j;
public: void get_ij (void);
void put_ij (void) ;};
class Y: public X {
int k;
public: int get_k (void);
void make_k (void) ;};
Функции–члены класса Y могут использовать функции get_ij, put_ij класса X, но не могут использовать ij, они не доступны для функций get_k и make_k класса Y.
Можно обеспечить доступ функции–члена класса Y к элементам класса X, описав их как protected. В тоже время ij остаются не доступными для остальной части программы.
Доступ наследуется к элементам, объявлённых защищёнными или общими, и не наследуются – для собственных private элементов.
При описании производного класса можно изменить статус доступа, наследуемым компонентам класса, с помощью модификатора статуса доступа.
Выглядит следующим образом:
class <имя производного класса> : <спецификатор доступа> <имя базового класса>
В качестве модификатора статуса доступа используются ключевые
слова protected, private и public. Спецификатор доступа может отсутствовать, и тогда по умолчанию считается public, если производный класс – структура и private, если производный класс – класс.
Если режим доступа public, то все общие и защищённые элементы базового класса остаются общими и защищёнными элементами производного класса. Если режим доступа private, то все общие и защищённые элементы базового класса
становятся private элементами производного класса. Если режим доступа protected, то общие и защищённые элементы базового класса становятся защищёнными элементами в производном классе.
Таким образом, в производных классах статус доступа компонентов класса может быть ужесточён.
6.5 Пример
#include <iostream> using namespace std; class X {
protected: int i, j;
public: void get_ij (void); void put_ij (void);};
class Y: public X { int k;
public: int get_k (void); void make_k (void) ;}; class Z: public Y {
public: void f (void) ;}; void X :: get_ij (void)
{
cout <<”Введите 2 числа:”; cin>>i>>j;
}
void X :: put_ij (void)
{
cout <<”i=”<<i<<”j=”<<j;
}
int Y :: get_k (void) {return k ;}
void Y :: make_k (void) {k=j+i ;}
void Z :: f (void) {i=2; j=3 ;} main ()
{
Z var; Z var2; var.get_ij (); var.put_ij (); var.make_k (); cout <<var.get_k (); var2.f (); var2.put_ij (); var2.make_k (); cout <<var.get_k ();