logo search
Шпора по информатике / 002

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

В больших программах часто возникает необходимость создания нового класса, который лишь немногими данными или функциями отличается от уже разработанного класса. Можно поступить следующим образом: переписать весь текст класса, дав ему новое имя, и затем внести в него необходимые исправления и дополнения. Но если изменений немного, то такой путь нерационален, так как приходится повторять много одинаковых операторов, что существенно удлиняет программу и затрудняет понимание её логики.

Н а с л е д о в а н и е - это использование классов-родителей (предков) для создания

классов-потомков. Эти классы называют также базовым и производным.

Создав какой-либо класс, можно создавать его модифицированные версии,

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

В С++ каждый класс может иметь сколько угодно потомков и родителей.

Связи классов представляются в виде иерархической структуры - дерева, похожего на деревья родословных (см. рис. 14.3 лекции 14). Возможна здесь и другая терминология:

класс-родитель = базовый класс = надкласс = суперкласс;

класс-потомок = производный класс = подкласс = субкласс.

Далее будем использовать термины: базовый и производный классы.

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

class circle : public figure

{ . . . дополнительные операторы нового класса . . . } ;

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

Если его нет, то по умолчанию все унаследованные элементы получают доступ private .

Появление слов public или protected означает, что доступ изменён. Существуют сложные правила о результатах сочетания двух модификаторов: доступа к элементам в базовом классе и доступа в заголовке производного класса, но рассматривать их не будем (см. [Подбельский], c.341).

Проиллюстрируем применение наследования в программе для задачи с фигурой, прямоугольником и овалом из раздела 14.1. Для упрощения овал заменим кругом, что позволит легко вычислять площади обеих фигур.

Ради компактности программы резко уменьшим количество данных в классах фигура, прямоугольник, круг по сравнению с реальными задачами: оставим только два значения ( x, y) , определяющие размер фигуры. Для размеров x, y всех фигур выберем тип double (двойная точность), который характерен в С++ для представления вещественных чисел внутри ЭВМ. Напомним, что каждое значение типа double требует для хранения 8 байтов в отличие от 4 байтов для float.

Количество методов в классах также будет мало. Каждая фигура будет «уметь» лишь устанавливать свои размеры ( функция setdim ) и показывать свою площадь при этих размерах ( функция area ).

Выберем имена для классов: figure, rectangle, circle.

Класс figure будет базовым, а два других класса производными от него в соответствии с рис. 13.2 лекции 13.

В добавление к модификаторам private, public из раздела 15.2 потребуется третий модификатор – protected ( означает защищенный), который разрешает доступ «наследникам» к собственным элементам базового класса, т.е. к private – данным.

Все методы базового класса объявим как общедоступные (public).

// d:\Proba\ lect16 _area.cpp **** 27.9.2003

#include <iostream.h>

#include <conio.h>

class figure {

protected: double x, y;

public: void setdim (double , double ) ;

virtual void area( ) { cout<< "Unknown area here!!!!!!\n"; }

};

void figure : : setdim (double i, double j=0) {

x = i; y = j; }

// **** new class - - - - - - - - - - - - - - - - - -

class rectangle : public figure {

public:

void area ( )

{ cout<<" Rectangle with x= "<< x << " y= "<<y;

cout<<" Its area= " << x * y << endl; }

};

// **** new class - - - - - - - - - - - - - - - - - -

class circle : public figure {

public:

void area ( ) {

cout<<" Circle with x= " << x ;

cout<<" Its area = " << 3.141593 * x * x <<endl; }

} ;

// - - - - - - - - - - - - - - - - - -

void main ( ) {

figure f, *p ;

rectangle r1; circle c1;

p=&f; p-> setdim(1, 2.); p -> area ( ) ;

p=&r1; p-> setdim(5, 6.); p -> area ( ) ;

p=&c1; p-> setdim(7, 8); p -> area ( ) ;

p=&c1; p-> setdim( 7 ); p -> area ( ) ;

r1.setdim (9, 9.); r1.area ( );

с1.setdim (1, 0.); c1.area ( );

getch( ); }

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

Используем новое слово - virtual - для функции базового класса.

Virtual =фактический. Оно показывает, что в производном классе эта функция может быть изменена без изменения ее заголовка. Классы rectangle и circle являются производными от класса figure, что и показано в их заголовке. Производный класс может использовать лишь общедоступные (public) и защищенные (protected) элементы базового класса.

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

Unknown area here!!!!!!

Rectangle with x=5 y=6 Its area = 30

Circle with x=7 Its area = 153.938

Circle with x=7 Its area = 153.938

Rectangle with x=9 y=9 Its area = 81

Circle with x=1 Its area = 3.14159