Полиморфизм

Содержание

Слайд 2

Пример. Использование раннего связывания. Пусть функции выполняют одинаковые по смыслу операции

Пример. Использование раннего связывания. Пусть функции выполняют одинаковые по смыслу операции

– выводят главные поля объекта на экран, следовательно, их можно назвать одним именем, например print().
Метод print(), таким образом, станет полиморфным – переопределяемым в
производном классе. Отдельное определение метода в своем классе называют аспектом полиморфного метода.
Слайд 3

Сложный полиморфизм Однако использование переопределенных методов не всегда безопасно. Известны случаи,

Сложный полиморфизм

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

при переопределении методов возникают ошибки, связанные с некорректным определением типа объекта на этапе компиляции, а следовательно и требуемого аспекта вызываемого метода.
Пример.
Для демострации ошибок, возникающих при некорректном
использовании простого полиморфизма введем в описание базового класса предыдущего примера новый метод show(). Этот метод будет вызывать статический полиморфный метод print() и наследоваться в производных классах. Кроме этого добавим в программу внешнюю функцию show_ext() с параметром – ссылкой на базовый класс, чтобы показать особенности раннего связывания.
Слайд 4

Слайд 5

Слайд 6

Виртуальные функции – определение Виртуальная функция задается точно также как и

Виртуальные функции – определение

Виртуальная функция задается точно также как и

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

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

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

Слайд 8

Доступ через указатель без использования виртуальных функций

Доступ через указатель без использования виртуальных функций

Слайд 9

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

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

Слайд 10

Доступ через указатель к виртуальным функциям

Доступ через указатель к виртуальным функциям

Слайд 11

Виртуальные функции и полиморфизм С понятием виртуальных функций тесно связано такое

Виртуальные функции и полиморфизм

С понятием виртуальных функций тесно связано такое понятие

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

Виртуальные функции - пример #include using namespace std; class B {

Виртуальные функции - пример

#include
using namespace std;
class B {
public:
virtual void

f(int) { cout << " fB(int)\n"; }
virtual void f(double) { cout << " fB(dbl)\n"; }
};
class D : public B {
public:
virtual void f(int) { cout << " fD(int)\n"; }
};
int main()
{
D d;
B b, *pb = &d;
b.f(6);
d.f(6);
b.f(6.2);
d.f(6.2);
pb->f(6);
pb->f(6.2);
}

Результат выполнения программы:

Слайд 13

Виртуальные функции – пример 2 #include using namespace std; class A

Виртуальные функции – пример 2

#include
using namespace std;
class A {
public:
virtual

void print() { cout << " Base A\n”; }
};
class D : public A {
public:
void print() { cout << " Derived B \n”; }
};
class D1 : public A {
public:
void fun() { cout << " Nothing\n”; }
};

int main()
{
A obj1;
D obj2;
D1 obj3;
A *ptr;
ptr = &obj1; ptr->print();
ptr = &obj2; ptr->print();
p = &obj3; p->print(); p->fun();
}

Где ошибка?

Слайд 14

Виртуальные функции – пример 2’ #include using namespace std; class A

Виртуальные функции – пример 2’

#include
using namespace std;
class A {
public:
virtual

void print(){cout << " Base A\n”; }
};
class D : public A {
public:
void print(){cout << " DerivedB \n”; }
};
class D1 : public A {
public:
void fun(){cout << " Nothing\n”; }
};

void main()
{
A obj1; D obj2; D1 obj3; A *ptr; D1 *p;
ptr = &obj1; ptr->print();
ptr = &obj2; ptr->print();
p = &obj3; p->print(); p->fun();
}

Результат выполнения программы:

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

Слайд 15

Чисто виртуальные функции Часто возникают ситуации, при которых виртуальные функции, определенные

Чисто виртуальные функции

Часто возникают ситуации, при которых виртуальные функции, определенные в

базовых классах не используются, а иногда и не содержат никаких действий, а являются лишь шаблонами для конкретных реализаций виртуальных функций в производных классах.
Для того, чтобы подчеркнуть, что в программе не предусматривается вызов виртуальной функции для базового класса, используются чисто виртуальные функции.
Для их определения используют следующую запись:
virtual возвращаемый_тип имя_функции (аргументы) =0;
Данная запись при компиляции воспринимается как отсутствие определения виртуальной функции для базового класса.
Вызов такой функции будет восприниматься как ошибка.
Если в базовом классе определен прототип чисто виртуальной функции, то она должна быть обязательно определена для всех производных классов.
Если в базовом классе определена хотя бы одна чисто виртуальная функция, то такой класс называется абстрактным базовым классом. Для такого класса нельзя создать экземпляр.
Слайд 16

Абстрактные классы и чистые виртуальные функции

Абстрактные классы и чистые виртуальные функции

Слайд 17

Раннее и позднее связывание Особый интерес к использованию виртуальных функций появляется

Раннее и позднее связывание

Особый интерес к использованию виртуальных функций появляется при

обслуживании случайных событий. Яркий пример – ожидание ввода с клавиатуры. В зависимости от того, каков будет ввод данных, к примеру, должны будут вызываться виртуальные функции из разных производных классов с использованием указателя на базовый.
Под процессами раннего связывания понимают те процессы, которые могут быть предопределены на этапе компиляции. Пример – при использовании вызова обычных функций. Компилятор заранее знает адрес вызываемой функции и помещает его в место вызова этой функции.
Позднее связывание реализуется при вызовах виртуальных функций. Процессы, относящиеся к позднему связыванию, определяются только на стадии выполнения программы.
Так, например, компилятор заранее может не предугадать вызов виртуальной функции для конкретных экземпляров производных классов.
Адрес виртуальной функции вычисляется только при работе программы.
Слайд 18

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

Виртуальные деструкторы

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

при выделении динамической памяти под объекты производных классов
Слайд 19

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

Виртуальные деструкторы

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

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

Как и для виртуальных функций, если в производном классе не описан деструктор, то при удалении объекта будет вызван деструктор базового класса. Если деструктор описан, то сперва произойдет вызов деструктора производного, а затем базового класса.

Слайд 20

Слайд 21

Visual C++. MFC. Программирование циклических процессов Переменная x меняется от xn

Visual C++. MFC. Программирование циклических процессов

Переменная x меняется от xn

до xk с шагом dx. Вывести таблицу значений x и y=esin(x), вычислить сумму и произведение y.

Создадим диалоговое окно (мое имеет название ListBProg) и разместим на нем следующие компоненты:
3 метки (Static Text);
3 поля ввода (Edit Control);
1 список (List Box);
3 кнопки (Button).

Слайд 22

Добавим для полей ввода (Edit Control) переменные m_edit_xn, m_edit_xk,m_edit_dx, которые будут

Добавим для полей ввода (Edit Control) переменные m_edit_xn, m_edit_xk,m_edit_dx, которые

будут возвращать значение float. В функцию кнопки «Выход» впишем строку OnOK() для того чтобы программа завершала свою работу по нажатию соответствующей клавиши.
Изменим ID кнопок «Решить» и «Очистить» на ID_Solve и ID_Clear, а также добавим переменную m_Result для компонента List Box
Слайд 23

Установите в свойствах List Box значение «Сортировка» в false, так как

Установите в свойствах List Box значение «Сортировка» в false, так как

иначе содержимое будет сортироваться по алфавиту, а «С полосой прокрутки» в true.
Слайд 24

Зададим начальные значения полей ввода. Для этого откроем файл *Dlg.cpp и

Зададим начальные значения полей ввода. Для этого откроем файл *Dlg.cpp и

в функцию OnInitDialog перед оператором return впишем следующий код:

Щелкаем по кнопке «Решить» и вводим следующий текст.