Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Курс лекцій.doc
Скачиваний:
15
Добавлен:
03.11.2018
Размер:
1.12 Mб
Скачать

8 Вiртуальнi функцiї та класи

8.1 Віртуальні функції.

У всiх прикладах, наведених раніше, повiдомлення, яке посилалось об'єкту, зв'язувалося з конкретним методом на етапi компiляцiї. Раннє зв'язування має переваги, пов'язанi з оптимальнicтю коду. Поряд iз раннiм зв'язуванням в багатьох об'єктно-орiєнтованих мовах пiдтримується механiзм пiзнього зв'язування, тобто коли повiдомлення зв'язується з конкретним методом на етапi виконання програми. Iншими словами, визначається набiр рiзних реакцiй об'єкту на одне i теж повiдомлення, а вже конкретна реакцiя визначається на етапi виконання програми.

Розглянемо, як реалiзована в С++ концепція полiморфiзму за допомогою механiзму вiртуальних функцiй. Вiртуальна функцiя-це специфiчна функцiя-член класу, яка визначається в базовому класi i потiм перевизначається в похiдних . Cукупнiсть класiв, в яких визначається та перевизначається конкретна вiртуальна функцiя називається полiморфiчним кластером, асоцiйованим з даною вiртуальною функцiїю.

Пiдтримка механiзму вiртуальних функцiй в C++ забезпечується спе -цифiчною властивiстю вказiвника на клас. Ця властивiсть полягає в тому, що вказiвник на базовий клас може вказувати не тiльки на об'єкт базового класу, але й на об'єкти будь-якого public-похiдного класу.

Нехай А-деякий базовий клас.

class A{ };

class B:public A{ }

class C:public B{ }

main( )

{A*ра;//-вказiвник на базовий клас;

B*pb;

C*pc;

pb=new B;

pa=pb;

Визначимо екземпляри об'єктiв класу:

main( )

{A a;

B b;

C c;

pa=&b; //вказiвнику на клас A присвоїмо адресу

pa=&c; //вказiвника на В. помилки немає

pb=&а; //компiлятор видасть повiдомлення про помилку.

Для зворотнього присвоєння потрiбно використовувати операцiю явного приведення типу. Приклад:

pb=pa; //помилка

pb=(B*)pa; //-безпомилково

pb=(B*)&a; //-помилка

pb=(B)&a; //-вiрно.

Розглянемо наступний приклад:

class TValue {

protected:

int value;

public:

TValue(int n){

value=n;}

int Getvalue(void)

{return value;}}

Визначимо похiдний клас;

class TMult:public TValue

{protected:

int mult;

public:

TMult(int n,int m):

TValue(n)

{mult=m;}

int GetValue(void)

{return value*mult;}

main( )

{ TValue*base;

base=TMult(10,2)

cout<<base->GetValue;}

//буде надрукованo 10 за рахунок

//механiзму раннього зв'зування

Вiртуальна функцiя-це звичайна функцiя-член класу, яка вiдрiзняється вiд iнших лише наявнiстю ключового слова virtual перед оголошенням функцiї:

virtual int GetValue( )

{ //...}

Якщо оголосити функцію GetValue( ) в обох класах як віртуальну, то в наведеному вище прикладі буде надруковано 20.

Оскiльки вiртуальна функцiя є членом класу, то її визначення може знаходитись за межами формального опису класу. При цьому при визначеннi функцiї ключове слово virtual не потрiбне.

В межах формального опису класу може бути оголошена віртуальна функція

virtual int GetValue( );

Тоді за межами формального опису:

int TValue::GetValue( )

{return value;}

Але чи потрiбне ключове слово virtual при оголошенні функції в усiх членах полiморфiчного кластеру? Виявляється, що достатнім є його наявність лише в першому члені поліморфічного кластера, перед віртуальною функцією самого високого рівня:

class A{

public:

virtual void vf( );};

class B:public A{

public:

void vf( );};

class С:public B{

public:

void vf( );

};

Розглянемо приклад використання вiртуальних функцiй. Нехай необхідно написати програму виводу на екран різних геометричних фігур. Причому проблема полягає в тому, що ми взагалі не знаємо на даному етапі, скільки цих фігур буде, як їх будувати. Але всі фігури мають спільне - вони є геометричними фігурами. Тому використаємо цей факт, написавши деякий клас Shape :

class Shape {

public:

virtual void Draw( );

};

Далі можемо визначити масив вказівників на Shape:

Shape * picture[100];

Тоді для побудови конкретної фігури будемо визначати класи, похідні від Shape, які містять віртуальні функції Draw( ). При такому підході main() -функцію можемо написати зразу, не чекаючи опису конкретних класів з функціями , що малюють фігури:

main( )

{int i=0;

while(i<100&& picture[i]!=0)

{picture[i]->Draw(); i+=1}}

Все, основна програма написана. А далі можемо писати собі похідні класи, скільки завгодно:

class Circle:public Shape

{public:virtual void Draw( )

{\\тіло}

};

class Line:public Shape

{public:virtual void Draw( )

{\\тіло}

};

Тоді виділяємо пам’ять,

picture[0]=new Line;

picture[1]=new Line;

picture[2]=new Circle;

і програма готова !