Приклад використання віртуальних функцій 


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



ЗНАЕТЕ ЛИ ВЫ?

Приклад використання віртуальних функцій



Тепер, вже знаючи, що таке віртуальні функції, спробуємо їх використати. В програмі 20.18 попередньої теми ми створили клас person. Наступна програма 13.4 використовуватиме той сам клас person, а також два нові класи: student і professor. Обидва ці похідні класи містять метод isoutstanding(), який дає змогу відібрати студенів з середнім балом, вищим за заданий, а також викладачів (професорів), з кількістю публікацій, більшою за задану.

 

 #include<iostream.h>

 #include<conio.h>

 #include<stdio.h>

 #include<bios.h>

 class person

 {protected:

 char name[40];

 public:

 void getname()

 {cout<<"Input name:"; cin>>name;}

 void putname()

 {cout<<"Name:"<<name<<endl;}

 virtual void getdata()=0;//Чиста віртуальна функція

 virtual int isoutstanding()=0;//Чиста віртуальна функція

 };

 /////////////

 class student:public person

 {private:

 float gps;

 public:

 void getdata()

 {person::getname();

 cout<<"Середній бал: ";cin>>gps;

 }

 int isoutstanding()

 {return(gps>3.5)?1:0;}

 };

 /////////////

 class professor: public person

 {private:

 int numpubs;

 public:

 void getdata()

 {person::getname();

 cout<<"Число публікацій:";cin>>numpubs;}

 

 int isoutstanding()

 {return(numpubs>100)?1:0;}

 };

 /////////////////

 

 int main()

 {clrscr();

 person* persPtr[100];

 char choice;

 int n=0;

 do{

 cout<<"Студент(s) чи професор(p):»;

 cin>>choice;

 if(choice==’s’)

 persPtr[n]=new student;

 else

 persPtr[n]=new professor;

 persPtr[n++]->getdata();

 cout<<”Continue y/n”;

 cin>>choice;

 } while (choice==’y’);

 for(int j=0;j<n;j++)

 {persPtr[j]->putname();

 if(persPtr[j]->isoutstanding())

 cout<<”Outrange\n”;

 }

 bioskey(0);

 return 0;

 }

Програма 13.4

Розберемо деякі особливості цієї програми.

 

Класи

Клас person – абстрактний, оскільки він містить чисті віртуальні функції.

virtual void getdata()=0;//Чиста віртуальна функція

virtual int isoutstanding()=0;//Чиста віртуальна функція

Ніякі об’єкти цього класу не можуть бути створені, він існує лише як базовий для класів student і professor. Ці породжені класи додають нові поля даних до базового класу: student – поле gps типу float, що містить середній бал студента; professor – поле numpubs цілого типу, що містить число публікацій педагога.

Функція isoutstanding()

Ця функція оголошена чистою віртуальною в класі person. В похідних класах вона оголошена як ціла і повертає значення 1 у випадку виконання заданої умови, та 0 – в іншому випадку.

 

Головна функція

В головній функції ми спершу даємо користувачу змогу ввести кілька імен учнів та педагогів, а також середнього балу для учня і числа публікацій для педагога. Після того, як користувач завершить ввід, відповівши “n” на питання про продовження, Програма виведе на екран імена всіх учнів та педагогів, притому для тих, хто перевершує певні критерії, виведеться також фраза «Outrange».

 

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

Деструктори базового класу обов’язково повинні бути віртуальними. Припустімо, щоб видалити об’єкт породженого класу, ми виконуємо delete щодо вказівника базового класу, що вказує на породжений клас. Якщо деструктор базового класу не є віртуальним, тоді delete, будучи звичайним методом, викличе деструктор для базового класу замість того, щоб запустити деструктор для породженого класу. Це призведе до того, що буде видалена тільки та частина об’єкту, яка відноситься до базового класу.  Це демонструє програма 13.5.

 #include<iostream.h>

 #include<conio.h>

 #include<stdio.h>

 #include<bios.h>

 ///////////////

 class Base

 {public:

 ~Base()

// virtual ~Base()

 {cout<<”Base remove\n”;}

 };

 ////////////

 class Derv:public Base

 {public:

 ~Derv()

 {cout<<”Derv remove\n”;}

 };

 /////////////

 int main()

 {clrscr();

 Base* pBase=new Derv;

 delete pBase;

 bioskey(0);

 return 0;

 }

Програма 13.5

 

Програма видасть такий результат

Base remove

Це означає, що деструктор для Derv не викликався взагалі! До такого результату призвело те, що деструктор базового класу в приведеному лістінгу невіртуальний. Виправити це можна, якщо зробити його віртуальним, закоментувавши перший рядок означення деструктора і активувавши другий:

//~Base()

virtual ~Base()

Тепер результат програми виглядатиме:

Derv remove

Base remove

Тільки тепер обидві частини об’єкту породженого класу видалені коректно.. Звичайно, якщо жоден з деструкторів не робить нічого важливого, а, наприклад, просто вивільняє пам’ять, виділену командою new, тоді їх віртуальність не є особливо потрібною. Але в загальному випадку, якщо треба бути певним в тому, що об’єкти породжених класів видаляються так, як потрібно, слід завжди робити деструктори базових класів віртуальними.

 

Віртуальні базові класи

Віртуальні базові класи дають змогу розв’язати проблему, яка виникає при множинному успадковуванні.

Розглянемо таку ситуацію: є базовий клас Parent, два похідні від нього класу Child1 та Child2 і ще четвертий клас GrandChild, породжений одночасно класами Child1 і Child2.

В такому випадку проблеми виникатимуть, якщо метод класу GrandChild захоче дістати доступ до даних чи методів класу Parent. Це продемонстровано в програмі 13.6

 

 #include<iostream.h>

 #include<conio.h>

 #include<stdio.h>

 #include<bios.h>

 class Parent

 {protected:

 int basedata;

 };

 //////////////

 class Child1:public Parent

 { };

 /////////////

 class Child2:public Parent

 { };

 ///////////

 class GrandChild:public Child1,public Child2

 {public:

 int getdata()

 {return basedata;}//error

};

 ///////////////

 int main()

 {clrscr();

 

 bioskey(0);

 return 0;

 }

Програма 13.6

При спробі прокомпілювати цю програму ми одержуємо повідомлення про помилку. Вона спричинена тим, що кожен з породжених класів Child1 і Child2 успадковує свою копію базового класу Parent. Ця копія називається підоб’єктом. Кожен з двох підоб’єктів містить власну копію даних базового класу, включаючи basedata. Потім, коли GrandChild посилається на basedata, то до якої з двох копій базового класу він здобуває доступ? Ситуація неоднозначна, про що компілятор і повідомляє.

Для усунення неоднозначності зробимо Child1 і Child2 спадкоємцями віртуального базового класу, як показано в програмі 13.7

 #include<iostream.h>

 #include<conio.h>

 #include<stdio.h>

 #include<bios.h>

 class Parent

 {protected:

 int basedata;

 };

 //////////////

 class Child1:virtual public Parent // успадковує копію Parent

 { };

 /////////////

 class Child2:virtual public Parent //успадковує копію Parent

 { };

 ///////////

 class GrandChild:public Child1,public Child2

 {public:

 int getdata()

 {return basedata;}//так можна

};

 ///////////////

 int main()

 {clrscr();

 

 bioskey(0);

 return 0;

 }

Програма 13.7

Використання ключового слова virtual в цих двох класах призводить до того, що вони успадковують єдиний спільний підоб’єкт базового класу Parent. оскільки тепер у нас є тільки одна копія basedata, неоднозначність при звертанні до базового класу усувається.

Необхідність використання віртуального базового класу показує, що при множинному успадкуванні виникає ряд концептуальних проблем, тому такий підхід слід використовувати обережно.

 

Дружні функції

Принцип інкапсуляції і обмеження доступу до даних забороняє функціям, що не є методами відповідних класів, доступ до прихованих (private) чи захищених даних об’єкту. Політика цих принципів ООП така, що, коли функція не є членом об’єкту, то вона не може користуватися певним рядом даних. Тим не менше, існують ситуації, коли така жорстка дискримінація призводить до значних незручностей.

 



Поделиться:


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

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