Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
5fan_ru_Obektno-orientirovannoe_programmirovan...doc
Скачиваний:
0
Добавлен:
01.05.2025
Размер:
351.74 Кб
Скачать

5. Производные классы

5.1. Наследование свойств как принцип ООП. Объявление производного класса

Язык С++ позволяет классу наследовать элементы данных и элементы-функции одного или нескольких других классов. Другими словами, новый класс может получить атрибуты и поведение от уже существующего класса.

Таким образом создается иерархия типов данных (классов). Имеющиеся классы –называют базовыми, а новые – производными (наследниками). Производные классы получают ,,наследство,, -данные и методы своих базовых классов и пополняются собственными данными и методами. В свою очередь, производный класс может служить базовым для другого класса.

Наследование позволяет абстрагировать некоторое общее или схожее поведение различных объектов в одном базовом классе.

Основная форма синтаксиса объявления производного класса:

сlass имя _производного _класса : < режим доступа >

имя _ базового _ класса

{

//............

};

Пример:

class A {

//…

};

class X: public A

{

//…

};

Здесь A – базовый класс, X- производный.

5.2. Режимы доступа при наследовании

Режим определяет уровень доступа к элементам базового класса внутри производного. При объявлении производного класса он не является обязательным и может быть privaite, public, protected.

Если режим доступа опущен, то по умолчанию это privaite. В таблице 5.1 описаны основные режима доступа при наследовании.

Таблица 5.1.Режимы доступа при наследовании.

Режим доступа в базовом классе.

Спецификатор доступа.

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

protected

public

public

protected

public

private

public

Не доступны

protected

public

private

private

protected

public

protected

protected

protected

private

protected

Не доступны

private

private

Не доступны

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

Пример:

// Базовый класс

сlass B{

int a;

pubic:

int b,c;

Int Bfunc( );

};

// Производный класс.

сlass X: public B{

int d;

public:

int e;

Int Xfunc( );

};

// Внешняя функция

Int Efunc( X & x1 );

Xfunc( ) имеет доступ :

  • к b,c Bfunc() – наследуются как public – компоненты;

  • к d,e, Xfunc() – собственные компоненты;

Efunc()имеет доступ :

  • из класса B: к b,c, Bfunc() как public – компонентам;

  • из класса X: к e, Xfunc()

Даже если открытые члены базового класса становятся закрытыми при наследовании с использованием спецификатора private, они по-прежнему доступны внутри производного класса. Рассмотрим пример программы из листинга 5.1.

Листинг 5.1.

// Пример наследования со спецификатором private

#include <iostream.h>

// базовый класс

class base {

int x;

public:

void setx(int n) { x = n; }

void showx() { cout << "x="<< x << '\n'; }

};

// Наследование через private

class derived : private base {

int y;

public:

// setx доступна внутри derived

void setxy(int n, int m) { setx(n); y = m; }

// showx доступна внутри derived

void showxy() { showx(); cout <<"y="<< y << '\n'; }

};

main()

{

derived ob;

ob.setxy(10, 20);

ob.showxy();

return 0;

}

Результаты работы этой программы:

x=10

y=20

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

5.3. Инициализация наследуемых членов

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

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

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

конструктор_производного_класса (список аргументов) : базовый класс1 (список аргументов),........, базовый класс N (список аргументов)

{...

// тело конструктора...........

};

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

Рассмотрим пример программы, показанной в листинге 5.2. В этой программе реализовано простое наследование, когда у производного класса имеется только один базовый.

Листинг 5.2. Простое наследование.

#include <iostream.h>

#include <stdlib.h>

#include <time.h>

// Базовый класс является моделью массива и содержит элементы-

// данные,описывающие свойства массива любого типа

//

class basearray

{

protected:

int maxitems; //максимальное число элементов

int citems; //число элементов, размещенных в массиве

public:

//конструктор

basearray(int nitems)

{

maxitems=nitems;citems=0;};

};

// производный класс в качестве элемента-данного содержит

// указатель на массив элементов конкретного типа

class iarray:public basearray

{

int *items; //указатель на массив

public:

//конструктор

iarray(int nitems);

//деструктор

~iarray();

//занесение элемента в массив

int putitem(int item);

//получение элемента из массива

int getitem(int ind,int &item);

//получение фактического числа элементов в массиве

int count() {return citems;};

};

//определение конструктора производного класса

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

// присутствует вызов конструктора базового класса

// : обеспечивает передачу параметров конструктору

// базового класса

iarray::iarray(int nitems):basearray(nitems)

{

items=new int[nitems];

}

//деструктор

iarray::~iarray()

{

delete items;

}

//занесение элемента в массив

int iarray::putitem(int item)

{

if(

citems<maxitems){

items[citems]=item;

citems++;

return 0;

}

else

return -1;

}

//получение элемента из массива

int iarray::getitem(int ind,int & item)

{

if(ind>=0 && ind<citems){

item=items[ind];

return 0;

}

else

return -1;

}

void main(void)

{

iarray m(100); //создан объект производного класса

int i,j,k;

int n;

randomize();

for(i=0;i<10;i++)

m.putitem(rand());

n = m.count();

for(i=0;i<n;i++){

m.getitem(i,j);

cout<<"индекс = "<<i<<" элемент = "<<j<<"\n";

}

}

Результаты работы этой программы:

индекс = 0 элемент = 27926

индекс = 1 элемент = 28543

индекс = 2 элемент = 22385

индекс = 3 элемент = 29846

индекс = 4 элемент = 27573

индекс = 5 элемент = 21881

индекс = 6 элемент = 23107

индекс = 7 элемент = 30123

индекс = 8 элемент = 28465

индекс = 9 элемент = 12165

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

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

Пример. Рассмотрим схему наследования, при которой производный класс прямо наследует два базовых :

BASE1 BASE 2

TOP

Class Base1

{

int x;

public:

Base1(int i) {x = i ; };

};

Class Base2

{

int x;

public:

Base2(int i): x(i){ };

};

Class Top : public Base1, public Base2

{

int a, b;

public:

Top(int i, int j) : Base1(i*5), Base2(j+i), a(i) {b = j; }

};

Здесь символ :, стоящий в определении производного класса перед именем конструктора базового класса обеспечивает механизм передачи параметров конструктору базового класса.

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

Top Z(1, 2);

При таком создании объекта Z производного класса вначале будет вызван конструктор первого базового класса Base1, что приведет к инициализации его собственного элемента-данного x значением 5, затем конструктор второго базового класса Base2, его собственный элемент данное x получит значение 3, и, наконец, будет вызван конструктор производного класса Top и его элементы данные получат значения а=1,b=2.

Пример законченной программы, иллюстрирующий множественное наследование, показан в листинге 5.3

Листинг 5.3. Множественное наследование.

#include <iostream.h>

class B1 {

public:

B1() { cout << "Работа конструктора B1\n"; }

~B1() { cout << "Работа деструктора B1\n"; }

};

class B2 {

int b;

public:

B2() { cout << "Работа конструктора B2\n"; }

~B2() { cout << "Работа деструктора B2\n"; }

};

// Наследование двух базовых классов.

class D : public B1, public B2 {

public:

D() { cout << "Работа конструктора D\n"; }

~D() { cout << "Работа деструктора D\n"; }

};

main()

{

D ob;

return 0;

}

Результаты работы этой программы:

Работа конструктора B1

Работа конструктора B2

Работа конструктора D

Работа деструктора D

Работа деструктора B2

Работа деструктора B1

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

Важно:

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

Контрольные вопросы

1.В чем сущность механизма наследования как принципа ООП?

2.Как объявляется производный класс?

3.Как осуществляется доступ к элементам базового класса из производного при наследовании?

4.Каким образом выполняется инициализация наследуемых членов?

5.Как выполняются конструкторы базовых классов при множественном наследовании?

6.СИСТЕМА ВВОДА –ВЫВОДА С++

6.1. Базовые положения системы ввода-вывода С++

С++ поддерживает все функции ввода-вывода языка С и, кроме того, определяет свою собственную объектно-ориентированную систему ввода-вывода Система ввода-вывода С++, так же, как система ввода-вывода С, действует через потоки.

Поток – это логическое устройство, которое выдает и принимает информацию.

Когда программа на С++ начинает выполняться, автоматически открываются четыре потока, показанные в таблице 6.1.

Таблица 6.1. Стандартные потоки С++.

Поток

Значение

Устройство по умолчанию

cin

Стандартный ввод

Клавиатура

cout

Стандартный вывод

Экран

cerr

Стандартная ошибка

Экран

clog

Буферизуемая версия cerr

Экран

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

Система ввода-вывода С++ поддерживается заголовочным файлом iostream.h. В этом файле задана иерархия классов.

Библиотека потоковых классов построена на основе двух базовых классов: ios и streambuf. Класс streambuf обеспечивает буферизацию данных во всех производных классах, которыми пользуется программист. Обращаться к его методам из прикладных программ обычно не нужно. Класс streambuf обеспечивает взаимодействие создаваемых потоков с физическими устройствами. Класс ios и производные классы содержат указатель на класс streambuf. Методы и данные класса streambuf программист явно не использует. Этот класс нужен другим классам библиотеки ввода-вывода.

Упрощенная схема иерархии потоковых классов показана на рис.6.1.

ios

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]