Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
ЯиМП.doc
Скачиваний:
4
Добавлен:
29.07.2019
Размер:
298.5 Кб
Скачать

Наследование

Лекция 3

Понятие наследования

Механизм наследования позволяет создавать иерархии классов таким образом, что один класс получает элементы другого класса и может дополнять или изменять их.

Класс, который наследуется, называется базовым или родительским классом. Наследующий класс называется производным классом. Базовый класс часто называют предком, производный – потомком.

Наследование бывает простым и множественным.

Простым называется наследование, при котором один или несколько производных классов наследуют элементы одного базового класса.

Множественным называется наследование, при котором производный класс наследует элементы нескольких базовых классов.

Синтаксис производного класса

Синтаксис создания производного класса при простом наследовании:

class имя : ключ_доступа имя_базового_класса

{ тело_класса };

Ключ доступа может иметь значения private, public, protected.

Для множественного наследования после двоеточия перечисляются через запятую все базовые классы со своими ключами доступа.

По умолчанию используется ключ доступа – private.

Спецификатор protected

Для элементов класса могут использоваться спецификаторы доступа private, public, protected.

Спецификатор protected определяет защищенную часть класса. Ее элементы являются доступными для любого производного класса, но недоступны для классов, не входящих в иерархию.

Доступ к элементам базового класса

Ключ доступа

Спецификатор в базовом классе

Доступ в производном классе

private

private

нет

protected

private

public

private

protected

private

нет

protected

protected

public

protected

public

private

нет

protected

protected

public

public

Пример

#include <iostream>

using namespace std;

class cl_A{int i;

protected: int k;

public: void set_i(int n) {i=n;}

void set_k(int n) {k=n;}

void show_i() { cout<<i<<endl;} };

class cl_B: public cl_A {int j;

public: void set_j(int n) {j=n;}

void show_j() { cout<<j<<endl;}

// void show_i_j() {cout<<i<<','<<j<<endl;}

void show_k() { cout<<k<<endl;} };

void main() { cl_B X;

X.set_i(10); X.show_i(); X.set_j(20); X.show_j();

X.set_k(30); X.show_k(); //cout<<X.k<<endl; }

Переопределение метода базового класса

Чтобы в производном классе переопределить метод, нужно объявить в нем метод с таким же именем.

#include <iostream>

using namespace std;

class cl_A{

public: void show() {cout<< "class A ";} };

class cl_B: public cl_A {

public: void show() {cl_A::show();

cout<< "class B ";} };

void main() {cl_B X;

X.show(); } //Результат : class A class B

Конструкторы и деструкторы при наследовании

Конструкторы и деструкторы не наследуются.

Конструктор базового класса выполняется первым, конструктор производного класса – вторым, и т.д.

Деструкторы выполняются в обратном порядке: деструктор производного класса – первым, деструктор базового класса – вторым.

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

Пример использования конструкторов с аргументами при наследовании

#include <iostream>

using namespace std;

class cl_A{int i;

public: cl_A(int n) {cout<<"constructor A"<<endl; i=n;}

~cl_A() {cout<<"destructor A"<<endl;}

void show_i() { cout<<i<<endl;} };

class cl_B: public cl_A {int j;

public: cl_B(int n,int m): cl_A(m)

{cout<<"constructor B"<<endl; j=n;}

~cl_B() {cout<<"destructor B"<<endl;}

void show_j() { cout<<j<<endl;} };

void main() { cl_B X(20,30);

X.show_i(); X.show_j(); }

Множественное наследование

Имеется 2 способа , позволяющие производному классу наследовать более одного базового класса:

  • создание иерархии классов;

  • прямое наследование нескольких базовых классов.

В случае создания иерархической структуры конструкторы выполняются в порядке наследования, деструкторы – в обратном порядке.

В случае наследования нескольких базовых классов конструкторы выполняются слева направо в том порядке, как заданы в объявлении производного класса. Деструкторы – в обратном порядке.

Пример множественного наследования двух классов

#include <iostream>

using namespace std;

class cl_A {int i;

public: cl_A(int n) {i=n;}

int get_i() {return i;} };

class cl_B {int j;

public: cl_B(int n) {j=n;}

int get_j() {return j;} };

class cl_C: public cl_A, public cl_B

{int k;

public: cl_C(int n, int m, int l): cl_A(l), cl_B(m) {k=n;}

void show() {cout<<get_i()<<','<<get_j()<<','<<k<<endl;} };

void main()

{cl_C X(10,20,30);

X.show(); // будет выведено: 30, 20, 10

}

Виртуальные функции

Виртуальной называется функция, вызов которой зависит от типа объекта.

Для объявления функции как виртуальной используется слово virtual.

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

Конструкторы не могут быть виртуальными, деструкторы – могут.

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

Сравнение виртуальных и перегружаемых функций

Виртуальная функция

  • Функции должны иметь одинаковую сигнатуру для всех версий;

  • должна быть членом класса;

  • версия функции выбирается на этапе выполнения.

Перегружаемая функция

  • Функции должны различаться типом и/или количеством параметров;

  • может не быть членом класса;

  • версия функции выбирается на этапе компиляции.

Указатели на базовый и производный классы

Указатель на базовый класс может ссылаться на объект этого класса или на объект любого класса, производного от этого базового. При этом доступ возможен лишь к тем элементам производного класса, которые были унаследованы от базового.

Указатель на производный класс может ссылаться на объекты производного класса и не может на объекты базового класса.

Пример использования указателя базового класса для доступа к элементам производного класса

#include <iostream>

using namespace std;

class Base{

public: virtual void show() {cout<< "Base ";} };

class cl_A: public Base {

public: void show() {cout<< "class A ";} };

class cl_B: public Base {

public: void show() {cout<< "class B ";} };

void main() {

Base *p; cl_A X; cl_B Y;

p=&X; p->show();

p=&Y; p->show();

}

Чисто виртуальная функция

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

Функция используется для того, чтобы реализацию выполнить позднее (в производном классе).

Объявление чисто виртуальной функции внутри базового класса имеет вид:

virtual тип имя_функц (список_парам) = 0;

Абстрактные классы

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

Абстрактный класс не может использоваться для объявления объектов, но он может применяться для объявления указателей, которым разрешен доступ к объектам, производным от абстрактного класса. Можно создавать ссылки на абстрактный класс.

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