Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

pragma once Hard / Занятие 5

.doc
Скачиваний:
14
Добавлен:
13.03.2015
Размер:
167.42 Кб
Скачать

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

Пример 5.1 Доступ к элементам класса

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

Ключ доступа

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

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

private

private

нет

protected

private

public

private

protected

private

нет

protected

protected

public

protected

public

private

нет

protected

protected

public

public

Создадим классы cl_A (базовый) и cl_B (производный) приведенные ниже:

#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;

}

Обратите внимание на строки, взятые в комментарий. Поле i в классе cl_B недоступно, так как в базовом классе оно private. Поле k в базовом классе protected, поэтому оно доступно в производном классе, но недоступно в функции main.

Пример 5.2 Конструкторы и деструкторы

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

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

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

Выполните пример, проанализируйте результаты. Обратите внимание на конструктор cl_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();

}

Пример 5.3 Множественное наследование

Имеются два способа, посредством которых производный класс может наследовать более одного базового класса:

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

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

В данном примере первым способом создан класс cl_E, вторым – класс cl_C.

Ниже приведены схемы классов и реализация.

#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;}

};

class cl_D: public cl_A {

int p;

public:

cl_D(int n, int m): cl_A(m) {p=n;}

int get_p() {return p;}

};

class cl_E: public cl_D {

int q;

public:

cl_E(int n, int m, int l): cl_D(m,l) {q=n;}

void show() {cout<<get_i()<<','<<get_p()<<','<<q<<endl;}

};

void main(){

cl_C X(10,20,30);

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

cl_E Y(100,200,300);

Y.show(); // будет выведено: 300, 200, 100

}

Пример 5.4 Виртуальный класс

Создадим следующую иерархию классов:

#include <iostream>

using namespace std;

class A {

public: int i;

};

class B1: public A {

public: int j;

};

class B2: public A {

public: int k;

};

class C: public B1, public B2 {

public:

void show() {cout<<i<<','<<j<<','<<k<<endl;}

};

void main(){

C X;

X.i=2; X.j=4; X.k=6;

X.show();

}

При компиляции этой программы возникнет ошибка «неоднозначный уровень доступа "i"». Ошибка происходит из-за того, что в классе C имеется два экземпляра класса A, и при обращении к полю i непонятно с каким из этих экземпляров нужно выполнять необходимые действия.

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

Правильный вариант программы и схема:

#include <iostream>

using namespace std;

class A {

public: int i;

};

class B1: virtual public A {

public: int j;

};

class B2: virtual public A {

public: int k;

};

class C: public B1, public B2 {

public:

void show() {cout<<i<<','<<j<<','<<k<<endl;}

};

void main(){

C X;

X.i=2; X.j=4; X.k=6;

X.show();

}

Пример 5.5 Виртуальная функция

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

#include <iostream>

using namespace std;

class Base{

public:

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 f(Base &a) {

a.show();

}

void main() {

Base *p; cl_A X; cl_B Y; Base Z;

p=&X; p->show(); cout<<endl;

p=&Y; p->show(); cout<<endl;

f(X); cout<<endl;

f(Z); cout<<endl;

}

Мы получили не тот результат, которого ожидали. Вместо метода show классов cl_A и cl_B все время вызывается метод show класса Base. Чтобы при использовании указателя (имеющего тип базового класса) ссылающегося на объект производного класса был вызван одноименный метод из производного класса, нужно объявить метод в базовом классе виртуальным.

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

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

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

class Base{

public:

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

};

Выполнив программу, вы увидите, что результат ее работы стал другим.

}Пример 5.6 Абстрактный класс

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

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

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

Рассмотрим пример абстрактного класса figure. Его метод area для каждого потомка должен быть свой, при этом метод должен быть у всех потомков обязательно. Поэтому метод объявлен в базовом классе как чисто виртуальный.

#include <iostream>

using namespace std;

class figure {

protected:double x;

public: figure(double n) {x=n;}

virtual double area () =0; //чисто виртуальная функция

double get_x () {return x;}

};

class square:public figure {

public: square(double a): figure(a) {}

double area () {return x*x;}

};

class circle:public figure {

public: circle(double r): figure(r) {}

double area () {const double Pi=3.14159;

return Pi*x*x;}

};

int main(){

figure *f;

square A(5);

circle B(2);

f=&A;

cout << "Area of square = " << f->area() << "; a=" << f->get_x() <<endl;

f=&B;

cout << "Area of circle = " << f->area() << "; r=" << f->get_x() << endl;

// cout<< f->x; ошибка

return 0;

}

Схема классов приведена на рисунке. Обратите внимание, что метод area класса figure выделен курсивом. Это является признаком, что он – чисто виртуальный.

10

Соседние файлы в папке pragma once Hard