Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
ЯП - ПОИТ (Бахтизин) часть 1 редакт.doc
Скачиваний:
0
Добавлен:
01.04.2025
Размер:
1.76 Mб
Скачать

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

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

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

class iList

{

public:

iList(); // Конструктор

~iList(); // Деструктор

struct TItem* add_item(int num);

int front_item_value();

void removet_item();

private:

struct TItem* front;

int size;

};

iList::iList() // Конструктор

{

size = 0;

front = 0;

}

iList::~iList() // Деструктор

{

while (size) remove_item();

}

// Остальные функции-члены

В данном примере для простоты подразумевается, что в точке объявления класса доступен, определенный ранее «элемент списка». Теперь структура программы такова:

  1. Подключение заголовочных файлов библиотек

  2. Определение типа «элемент списка»

  3. Определение функций работающих с элементами списка

  4. Класс iList

  5. Класс iStack

В связи с изменениями, произошедшими в реализации списка, придется модифицировать стек. Это можно сделать так:

// Класс iStack (стек чисел_типа_int)

class iStack

{

public:

void push(int a);

int pop();

private:

struct iList list; // Список

};

void iStack::push(int a)

{

list.add_item(a);

}

int iStack::pop()

{

int res = list.front_item_value();

list.remove_item();

return res;

}

Следует отметить, что в данном случае нет необходимости в создании конструктора и деструктора класса iStack, т.к. единственная переменная член (list) имеет конструктор и деструктор по умолчанию (т.е. конструктор и деструктор не требующие передачи им параметров), которые будут вызваны автоматически.

Уже на таком простом примере видно, что создание цепочки базирующихся друг на друге классов имеет большие преимущества по сравнению с созданием набора процедур, реализующих более простые сущности. Однако ООП предлагает еще более широкие возможности.

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

class iStack: public iList

{

public:

void push(int a);

int pop();

};

В данном случае класс iList становится предком для iStack и все его члены (кроме конструктора и деструктора) автоматически становятся членами класса-потомка. Определению, очевидно, подлежат функции-члены push() и pop():

void iStack::push(int a)

{

add_front_item(a);

}

int iStack::pop()

{

int res = get_front_item();

del_front_item();

return res;

}

Как и в предыдущем, в данном примере явно не вызываются конструкторы iList и iStack. Это объясняется тем, что первый имеет конструктор по умолчанию, а второму конструктор вообще не нужен (нет данных подлежащих инициализации или действий, которые нужно произвести перед началом работы с экземпляром класса).

Упрощенно наследованием классов называется порождение одного класса от другого. При этом порождающий класс является предком, отцом, базовым классом для порождаемого класса. И наоборот, порождаемый класс – наследник, сын, производный класс относительно порождающего класса. Возможны разные способы наследования. Простейший из них описывается так:

определение_класса_наследника ::=

“class” имя_потомка “: public” имя_предка

“{” { блок_объявлений } “};”

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