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


Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

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



«В С++ одним из наиболее туманных понятий, с точки зрения новичков, является полиморфизм. Само слово полиморфизм достаточно экзотично и, видимо, по этой причине оно прижилось, хотя есть и другие, может быть, более подходящие термины. Программисты – странный народ. Они часто стремятся сделать из мухи слона просто так, ради прикола. Существуют даже ежегодные состязания среди программистов по С, где первый приз получает тот, кто напишет самую труднопонимаемую программу. Программисты черпают информацию из книг, статей в журналах, материалов конференций и т.п. Чем больше людей интересуются языком, тем лучше. Странно звучащие термины скорее вызовут любопытство слушателя или читателя, чем обыденная речь. А любопытство, в свою очередь, стимулирует изучать новые свойства языка. Чем больше людей изучают язык, тем он популярнее. Именно так и случилось с С++ и термин полиморфизм привлек внимание не одного любопытного читателя»

Тэд Фейсон.

Полиморфизм –от греческих словpoly (много), morphos (форм) – множественность форм. Практически с понятием полиморфизма мы уже сталкивались – это перегрузка функций и операций. При наследовании оно тоже используется через понятие виртуальных функций.

 

Раннее (статическое) и позднее (динамическое) связывание

 

Вспомним пример классов A и B из п.1.

 

class A

{ private: int d1, d2, d3;

public:

  A() {d1 = d2 = d3 = 0;} // конструктор по умолчанию

A(int x, int y, int z)  {d1 = x; d2 = y; d3 = z;} // конструктор с аргументами

void  PrintA() {cout << d1 << ’ ‘ << d2 << ’ ‘ << d3;}

};

 

Определим новый класс B

 

class B: public A[,private A1,...]       // класс B порожден из класса A 

{ int  d3, d4;

public:

B();                 // конструктор без аргументов

   B(int, int, int);   // конструктор с аргументами

int  Sum3();

void  PrintB();  

};

 

Рассмотрим вариант, когда функции вывода член-данных в этих классах имеют одинаковое имя Print():

 

void  A:: Print() {cout << d1 << ’ ‘ << d2 << ’ ‘ << d3 << ’ ‘;}

void  B:: Print() {A:: Print(); cout << d3 << d4;}

 

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

 

  A  x;

B  y;

x.Print();   // Print() класса A

y.Print();   // Print() класса B

 

выбор функции компилятор определяет по типу объектов: x или y. Причем этот выбор осуществляется еще на этапе компиляции. Такое связывание вызова функции с конкретной функцией на этапе компиляции и называется ранним или статическим связыванием.

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

Например, пусть еще определен класс C:

 

 

class  C: public A

{int   d5;

public:

C() {d5 = 100;}

C(int  a, int  b, int c, int  d): A(a, b, c), d5(d)    { }

void   Print() {A:: Print(); cout << ’ ‘ << d5;}

 ...

};

 

и программа:

 

void  main()

{A  x; B  y(1, 2, 3, 4, 5, 6); C z; A * pa;

 int   k; cin >> k;

switch (k)

{case 0: pa = &x; break;

 case 1: pa = &y; break;

 case 2: pa = &z; break;

 default:: cout <<»СТОП!»; return;

}

pa –> Print();

...

}

 

В этом примере до исполнения программы неизвестно, адрес какого объекта будет занесен в указатель pa и, следовательно, функция Print() из какого класса – A, B или C будет вызвана. Другими словами, связать вызов функции Print() с конкретным экземпляром этой функции возможно только при выполнении программы по специальному алгоритму (см. п.5.5). Это так называемое позднее или динамическое связывание. Но в приведенном выше примере будет пока вызываться функция A::Print() и выводить базовую часть любого объекта. Чтобы происходил действительно вызов разных функций в зависимости от значения указателя pa, надо функцию Print() объявить виртуальной.



Поделиться:


Последнее изменение этой страницы: 2021-12-07; просмотров: 129; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 3.145.178.157 (0.005 с.)