Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Информатика 16 18 19.docx
Скачиваний:
6
Добавлен:
19.04.2015
Размер:
48.12 Кб
Скачать

16. Рекурсия функций.

В программировании рекурсия — вызов функции (процедуры) из неё же самой, непосредственно (простая рекурсия) или через другие функции (сложная или косвенная рекурсия), например, функция A вызывает функцию B, а функция B — функцию A. Количество вложенных вызовов функции или процедуры называется глубиной рекурсии.

Рекурсия — см. «Рекурсия».

Рекурсией программисты на своём мунспике называют вызов функцией самой себя прямо (в теле функции расположен вызов себя) или косвенно (функция вызывает функцию, которая вызывает функцию, которая… и в одной из этих функций расположен вызов самой первой функции). Типичным примером функций, использующих рекурсию, являются вычисление факториала и функция Аккермана. В более общем смысле, включение некоторой сущностью самой себя целиком (см. пример с зеркалами ниже).

Помимо своего матанского и программистского значения, рекурсия — мем, используемый в наших интернетах в основном среди читателей башорга. Более сложный вариант без гиперссылок: «Чтобы понять рекурсию, нужно сперва понять рекурсию».

18. Динамическая организация данных. Двоичные деревья.

Динамические структуры данных: двоичные деревья

Дерево — это совокупность элементов, называемых узлами (при этом один из них определен как корень), и отношений (родительский–дочерний), образующих иерархическую структуру узлов. Узлы могут являться величинами любого простого или структурированного типа, за исключением файлового. Узлы, которые не имеют ни одного последующего узла, называются листьями.

В двоичном (бинарном) дереве каждый узел может быть связан не более чем двумя другими узлами. Рекурсивно двоичное дерево определяется так: двоичное дерево бывает либо пустым (не содержит ни одного узла), либо содержит узел, называемый корнем, а также два независимых поддерева — левое поддерево и правое поддерево.

Двоичное дерево поиска может быть либо пустым, либо оно обладает таким свойством, что корневой элемент имеет большее значение узла, чем любой элемент в левом поддереве, и меньшее или равное, чем элементы в правом поддереве. Указанное свойство называется характеристическим свойством двоичного дерева поиска и выполняется для любого узла такого дерева, включая корень. Далее будем рассматривать только двоичные деревья поиска. Такое название двоичные деревья поиска получили по той причине, что скорость поиска в них примерно такая же, что и в отсортированных массивах: O(n) = C • log2n (в худшем случае O(n) = n).

Пример. Для набора данных 9, 44, 0, –7, 10, 6, –12, 45 построить двоичное дерево поиска.

Согласно определению двоичного дерева поиска число 9 помещаем в корень, все значения, меньшие его — на левое поддерево, большие или равные — на правое. В каждом поддереве очередной элемент можно рассматривать как корень и действовать по тому же алгоритму. В итоге получаем

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

Основные принципы ООП. Конструкторы и деструкторы.

19

В настоящее время в программировании существуют четыре принципиально отличающихся подхода:

· процедурный (алгоритмический),

· логический (декларативный),

· функциональный,

· объектно-ориентированный.

Объектно-ориентированный подход в разработке программ [10–12] появился позже других. Его появление стало следствием все возрастающей сложности программного обеспечения, необходимости его регулярного обновления из-за постоянно изменяющихся требований к программам и условиям их функционирования. Именно объектно-ориентированный подход позволяет решать задачи наивысшей сложности, повышает надежность программ, облегчает их модернизацию.

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

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

Пример абстракции:

Модель принтера

Имя:

HP_5P

Набор свойств:

Модель, год выпуска, состояние готовности

Значения свойств:

Модель = "HP 5P",

Год выпуска = 1999,

Состояние готовности=Ready (готов к печати)

Действия объекта:

Печатает

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

В программе свойства объекта представлены с помощью переменных, а действия объекта – с помощью подпрограмм (в C++ – функций). Подпрограммы, содержащие описания действий объекта, называются методами. Набор конкретных значений переменных, характеризующих объект, определяет состояние объекта. Объект имеет ограниченный набор допустимых состояний. Например, в нашей модели принтера возможны два состояния: «готов к печати» и «печатает». Остальные свойства задаются для конкретного принтера изначально и не изменяются в процессе работы программы.

Таким образом, объект в объектно-ориентированном программировании (ООП) – это совокупность переменных состояния и связанных с ними методов (операций). Эти методы, с одной стороны, определяют, как объект взаимодействует с внешним миром. С другой стороны, методы осуществляют переход объекта из одного состояния в другое, причем метод проверяет, возможен ли такой переход в данной ситуации. Например, принтеру дается команда отпечатать документ. Если принтер находился в этот момент в состоянии «готов к печати», изменит свое состояние на состояние «печатает». Если принтер в момент получения команды печатал другой документ, то он остается в прежнем состоянии «печатает», а наш документ ставится в очередь на печать. Когда принтер закончит печатать один документ, он приступит к печати нашего документа. Отпечатав наш документ, принтер проверит, есть ли еще документы в очереди на печать, и, если очередь пуста, перейдет в режим «готов к печати». Таким образом, в ответ на наше действие принтер сам определил свои дальнейшие действия, и его выбор зависел от предыдущего состояния принтера. Объектно-ориентированный подход позволяет объектам самим решать, как выполнить ту или иную задачу.

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

В процессе декомпозиции задачи могут быть выявлены объекты с одинаковыми наборами свойств и поведением. Например, для программы, моделирующей футбольный матч, необходимы две команды объектов-«футболистов». Каждого игрока матча можно охарактеризовать следующими свойствами: название команды, форма одежды, номер, координаты на поле. Поведение игроков на поле можно описать следующими действиями: стоит, бежит, отдает мяч, получает мяч. Объекты с одинаковым набором свойств и методов образуют класс. Класс – основа объектно-ориентированной методологии – служит для создания в программе любого количества объектов с заданным набором свойств и заданной моделью поведения. Если проводить параллель с алгоритмической методологией, то класс выступает в роли типа данных, однако это специфический тип, так как кроме данных в класс включаются действия (функции-члены класса, манипулирующие с данными).

Теперь рассмотрим основные термины ООП.

Класс (class) – это тип данных, включающий в себя группу данных различных типов (свойства) и методы работы с этими данными.

Объект (object) – это экземпляр класса, обладающий набором свойств с заданными значениями.

Свойство (property) – характеристика объекта, представленная в виде переменной, являющейся членом класса.

Метод (method) – это подпрограмма, входящая в состав класса и управляющая данными объекта.

Событие (event) – какое-либо происшествие в программе, системе, например, была нажата кнопка мыши.

Сообщение (message) – адресованная конкретному объекту информация о происшедшем событии; объект, получивший сообщение, должен на него ответить своими действиями, вызвав метод, соответствующий этому событию. Например, если получено сообщение о том, что была нажата левая кнопка мыши на пункте меню Exit, произойдет завершение программы, если был выбран пункт меню Save, будет выполнено сохранение информации.

Также к основным терминам ООП нужно отнести такие понятия, как инкапсуляция, наследование и полиморфизм, которые будут рассмотрены далее.

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

Рассмотрим создание класса пользователем и применение класса, созданного пользователем, в программе.

Объявление класса:

class <имя класса>

{

public:

<объявление переменных, констант, функций>

private:

<объявление переменных, констант, функций>

protected:

<объявление переменных, констант, функций>

};

Объявление класса начинается с ключевого слова class. Переменные, константы, функции, включенные в класс, называются членами класса. Члены класса объявляются в одном из трех разделов класса: public, private, protected. Включение члена класса в тот или иной раздел влияет на доступность этого члена для объектов других классов.

Раздел public обеспечивает доступность помещенных в него свойств и методов для любых объектов. Это внешняя часть класса, или интерфейс его взаимодействия с другими классами.

Раздел private обеспечивает сокрытие помещенных в него свойств и методов от других объектов, делая их недоступными объектам других классов. Таким образом, класс защищает свои данные (свое состояние) от внешнего воздействия. Посторонний объект может воздействовать на объект данного класса только через интерфейс этого класса.

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

Рассмотрим в качестве примера класс принтеров. Включим в его состав следующие свойства: модель, год выпуска и состояние принтера; спрячем эти свойства от постороннего воздействия, поместив в раздел private. Ограничимся двумя возможными состояниями принтера: 0 – принтер готов к работе, 1 – принтер печатает. Методы сделаем доступными для других объектов. Метод init_printer() позволит установить начальные значения свойствам принтера. Метод set_print() переведет принтер в состояние печати, если принтер до того находился в состоянии готовности, и не изменит его состояния, если принтер уже печатал. Метод stop_print() приводит к остановке печати. Метод show() отображает состояние принтера на экране:

class Printer

{

private:

char model[15]; //модель принтера

int year; //год выпуска

int status; //состояние принтера

public:

void init_printer(char* _model, int _year);

void set_print();

void stop_print();

void show();

};

Замечание. Если раздел private включен в объявлении класса первым, ключевое слово private можно опустить:

class Printer

{

char model[15]; //модель принтера

int year; //год выпуска

int status; //состояние принтера

public:

void init_printer(char*_model,int_year);

void set_print();

void stop_print();

void show();

};

Описание методов:

void Printer::init_printer(char* _model,int _year)

{

strcpy(model,_model); //инициализация свойства model

year=_year; //инициализация свойства year

status=0; //начальное состояние – готов к печати

}

void Printer :: set_print()

{

if (!status) status=1;

}

void Printer :: stop_print()

{

status = 0;

}

void Printer :: show()

{

cout<<"Model:"<<model<<"year:"<<year<<"status:"<<status<<endl;

}

В главную часть программы – функцию main() – включим следующие действия: создание объекта, инициализация свойств объекта, изменение состояния объекта и вывод его текущего состояния на экран:

int main(void)

{

Printer printer; //создание объекта

//инициализация свойств объекта

printer.init_printer("HP5P", 1999);

printer.show(); //вызов метода объекта

printer.set_print();

printer.show();

printer.set_print();

printer.show();

printer.stop_print();

printer.show();

return 0;

}

В результате работы программы на экране появятся 4 строки текста:

Model: HP5P year: 1999 status: 0,

Model: HP5P year: 1999 status: 1,

Model: HP5P year: 1999 status: 1,

Model: HP5P year: 1999 status: 0.

17.3. Конструкторы и деструкторы

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

Конструктор включается в состав класса в разделе public:

class Printer

{

public:

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

};

Конструктор выделяет память под объект, достаточную для размещения всего объекта, и инициализирует переменные состояния объекта:

Printer :: Printer()

{

strcpy(model,"Canon_BJC250");

year=2000;

status=0;

}

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

int main(void)

{

Printer printer; //Конструктор инициализирует

//переменные объекта

printer.show();

return 0;

}

При выполнении этой программы на экран будет выведено сообщение:

Model: Canon_BJC250 year: 2000 status: 0

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

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

class Printer

{

public:

//Конструктор с параметрами

Printer(char* _model, int _year);

};

где _model – имя параметра, соответствующего значению модели, _year – имя параметра, соответствующего значению года.

Замечание. Имена параметрам могут быть даны любые, но удобно, когда имена параметров совпадают с названиями соответствующих свойств, отличаясь от них лишь первым символом _ (знак подчеркивания). Такой прием позволяет не запутаться при написании конструктора. Однако применять его можно лишь в том случае, если компилятор позволяет ставить знак _ в начале идентификатора.

Теперь конструктор будет выглядеть так:

Printer :: Printer(char* _model, int _year)

{

strcpy(model, _model);

year=_year;

status=0;

}

Воспользуемся конструктором с параметрами для создания в программе двух объектов с различными свойствами:

int main(void)

{

//Создание первого объекта

Printer printer1("HP5P",1999);

printer1.show();

//Создание второго объекта

Printer printer2("Canon_BJC250",2000);

printer2.show();

return 0;

}

При выполнении этой программы на экран будет выведено сообщение:

Model: HP5P year: 1999 status: 0

Model: Canon_BJC250 year: 2000 status: 0

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

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

Для разрушения объекта используется метод, называемый деструктором. Для деструктора, также как и конструктора, не указывается тип возвращаемого значения, имя совпадает с именем класса, только перед именем помещается знак ~ (тильда).

Деструктор не имеет параметров и включается в раздел public:

class Printer

{

public:

Printer(char* _model, int _year); //Конструктор //с параметрами

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

};

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

Рассмотрим пример с классом Printer, где одно из свойств (название модели) помещается в динамической памяти:

class Printer

{

char* model; //указатель на строку, которая будет

//содержать название модели и разместится

//в динамической памяти

int year; //год выпуска

int status; //состояние принтера

public:

Printer(char* _model, int _year);

~Printer();

void set_print();

void stop_print();

void show();

};

Конструктор:

Printer :: Printer(char* _model, int _year)

{

int len=strlen(_model); //Определение длины

//строки

//Выделение памяти в динамической области

//для размещения строки и символа '\0'

model=new char[len+1];

strcpy(model, _model);

year=_year;

status=0;

}

Деструктор:

Printer :: ~Printer()

{

//Освобождение динамической памяти

delete[] model;

//Присваивание указателю значения пустого

//указателя, обязательно в Visual C++ 6.0

model = NULL;

}

Тогда программа, выполняющая все этапы работы с объектом, будет выглядеть так:

int main(void)

{

Printer printer("HP5P",1999); //Создание объекта

printer.show();

return 0;

}

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

К началу главы

17.4. Инкапсуляция

Инкапсуляция – это принцип ООП, объединяющий в одном классе данные и методы, манипулирующие этими данными и защищающие данные класса от внешнего воздействия.

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

Класс также защищает свое состояние (значения свойств) от несанкционированного изменения, это реализуется с помощью раздела класса private. Включенные в private свойства доступны только этому объекту, поэтому не могут быть изменены другими объектами. Для доступа к свойствам в раздел класса public включаются методы. Методы проверяют, возможно ли такое изменение состояния объекта под влиянием других объектов, и только после этого изменяют значения переменных состояния. Если переход в новое состояние невозможен, методы оставляют объект в прежнем состоянии.

Изменим пример с принтером. Предположим, во время профилактики вычислительной системы у принтера было отключено электропитание. Естественно, в этом случае наши попытки что-то напечатать ни к чему не приведут. Учтем это в нашей модели принтера. Добавим свойство is_on, отражающее подключение к принтеру электрического питания (0 – включено, 1 – выключено). Тогда состояние принтера определяется комбинацией значений двух свойств – is_on и status (табл. 17.1).

Таблица 17.1

Допустимые состояния принтера

is_on

status

Сообщение на экране

0

0

Принтер включен

1

0

Принтер включен Состояние: готов к работе

1

1

Принтер включен Состояние: печатает

При этом класс представляется в виде:

class Printer

{

char model[15];

int year;

int status;

int is_on; //Принтер включен (0 – нет,1 – да)

public:

Printer(char* _model, int _year);

void on_off(); //Включение/выключение питания

void set_print();

void stop_print();

void show();

};

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

Printer :: Printer(char* _model, int _year)

{

strcpy(model, _model);

year=_year;

status=0;

is_on=0; //Принтер отключен по умолчанию

}

void Printer :: on_off()

{

//Метод моделирует нажатие кнопки включения

//питания:если оно выключено, то произойдет

//включение, и наоборот;

//одновременно при любых переключениях питания //принтер оказывается в состоянии готовности

//и печать не ведет

is_on=!is_on;

status=0;

}

void Printer :: set_print()

{

If (is_on) status=1; //Принтер будет печатать,

//если он включен

}

void Printer :: stop_print()

{

If (status) status=0; //Принтер остановится,

//если он до того печатал

}

void Printer :: show()

{

cout << "Модель: " << model << "Год выпуска: " << year << endl;

if (is_on)

{

cout << "Принтер включен";

if (status) cout << "Состояние: печатает";

else cout << "Состояние: готов к работе";

}

else cout << "Принтер выключен";

cout << endl;

}

Класс запрещает прямой доступ к своим переменным состояния, поэтому не удастся реализовать недопустимое состояние is_on=0, status=1. Методы set_print(), stop_print(), on_off(), предоставляющие доступ к свойствам объекта, реализованы так, что также исключают это состояние объекта. В этом и заключается смысл понятия инкапсуляции.

К началу главы

17.5. Полиморфизм

Полиморфизм – это принцип ООП, позволяющий включать в состав класса несколько функций с одинаковыми именами, но выполняющих различные действия. Такие функции должны иметь различные списки параметров, они должны отличаться или количеством параметров в списках, или типами соответствующих параметров. Полиморфизм в ООП есть проявление свойства языка C++, называемого перегрузкой функций (п. 16.4).

Пример полиморфизма – класс графических примитивов Draw. Класс может проявить себя как текст, прямоугольник или окружность. Для этого в класс включены три метода show(), различающиеся параметрами.

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

#include <string.h>

#include <iostream.h>

class Draw

{

int x1, y1, x2, y2, r;

char *message;

public:

Draw();

virtual ~Draw();

void show();

void show(int xx1, int yy1, int xx2, int yy2);

void show(int xx1, int xx2, int rr);

};

Draw::Draw()

{

x1=x2=y2=y1=r=0;

message=new char[10];

strcpy(message, "text");

}

Draw::~Draw()

{

delete[] message;

message=NULL;

}

void Draw::show()

{

cout << message << endl;

}

void Draw::show(int xx1,int yy1,int xx2,int yy2)

{

cout << "Rectangle" << endl;

}

void Draw::show(int xx1,int xx2,int rr)

{

cout << "Circle" << endl;

}

int main(void)

{

Draw draw;

draw.show();

draw.show(1,1,20,10);

draw.show(10,10,10);

return 0;

}

В результате выполнения программы на экране появляются сообщения:

Text

Rectangle

Circle

К началу главы

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

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

Созданные с использованием механизма наследования новые классы называются классами-наследниками или классами-потомками.

//Объявление базового класса

class A

{

… //Члены класса A

};

//Объявление класса-наследника

class B : public A

{

… //Собственные члены класса B

};

Новые классы наследуют только те свойства и методы, которые объявлены в разделах public и protected базовых классов. При этом свойства и методы из раздела protected доступны только базовому классу и его наследникам, тогда как свойства и методы из раздела public доступны объектам любого класса (рис. 17.1). Исключением из этого правила являются конструкторы и деструкторы, которые не наследуются.

Рис. 17.1. Уровни доступа к членам класса

В состав класса B входят собственные члены и члены класса A из разделов public и protected.

Рассмотрим пример наследования, в качестве базового класса возьмем класс person, характеризующий любого человека, и включим в раздел protected свойство класса name – имя человека:

class person

{

protected:

char name[20];

};

Создадим на основе класса person класс-наследник student:

class student : public person

{

char department[20]; // факультет

public:

student(char* _name, char* _department);

void message();

};

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

student :: student(char* _name, char* _department)

{

strcpy(name, _name);

strcpy(department, _department);

}

//message – метод, сообщающий сведения об объекте

void student::message()

{

cout << "My name is " << name << ". I study at " << department << endl;

}

Как видно, методы класса student используют как собственное свойство, так и свойство, полученное по наследству.

Создание объекта класса student и вызов его метода ничем не отличаются от того, как это делается для класса без наследования:

int main(void)

{

student N("Nick", "SS,SK,VT");

N.message();

return 0;

}

Во время выполнения такой программы на экране появляется сообщение:

My name is Nick. I study at SS, SK, VT.

Теперь рассмотрим наследование методов базового класса, включим в базовый класс конструктор, инициализирующий собственное свойство базового класса значением "Noname", и метод message, сообщающий информацию о классе:

class person

{

protected:

char name[20];

public:

person();

void message();

};

person :: person()

{

strcpy(name, "Noname");

}

void person::message()

{

cout << "My name is " << name << endl;

}

Таким образом, в класс-наследник student теперь включены два метода message, имеющие одинаковый тип и одинаковый список параметров – один из базового класса, другой – собственный. Собственный метод доступен через имя объекта. Однако можно получить доступ к унаследованному методу, если использовать для доступа к методу указатель соответствующего типа – указатель на базовый класс:

int main(void)

{

person A;

student B("Ann", "SS,SK,VT");

A.message();

B.message();

person* pperson = &B;

pperson->message();

student* pstudent = &B;

pstudent->message();

return 0;

}

На экране увидим сообщения:

Noname Сообщение об= объекте A

Ann SS,SK,VT Сообщение об объекте B (используется

собственный метод message)

Ann Сообщение об объекте B (используется

метод message, полученный по наследству)

Ann SS,SK,VT Сообщение об объекте B (используется

собственный метод message)

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

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

class person

{

protected:

char name[20];

public:

person();

virtual void message();

};

Теперь результатом выполнения программы будут следующие сообщения:

Noname Сообщение об объекте A

Ann SS,SK,VT Сообщение об объекте B (используется

собственный метод message)

Ann SS,SK,VT Сообщение об объекте B (используется

собственный метод message)

Ann SS,SK,VT Сообщение об объекте B (используется

собственный метод message)

С помощью виртуальных функций можно создать класс-наследник, имеющий тот же интерфейс, что и базовый класс, но обладающий своей собственной моделью поведения. Механизм виртуальных функций реализуется следующим образом: обычно обработка вызовов функций выполняется на этапе компиляции и завершается на этапе редактирования связей, когда вызов метода жестко связывается с соответствующей функцией (раннее связывание); если метод объявлен как виртуальный, выполняется так называемое позднее связывание, т. е. связывание вызова и функции во время выполнения программы.

К началу главы