Віртуальні функції.
До механізму віртуальних функцій звертаються в тих випадках, коли в кожному похідному класі потрібно свій варіант деякої компонентної функції. Класи, що включають такі функції, називаються поліморфними і відіграють особливу роль в ООП. Віртуальні функції надають механізм пізнього (відкладеного) чи динамічного зв'язування. Будь-яка нестатична функція базового класу може бути зроблена віртуальною, для чого використовується ключове слово virtual. Приклад. class base { public: virtual void print(){cout<<“\nbase”;} . . . }; class dir : public base { public: void print(){cout<<“\ndir”;} }; void main() { base B,*bp = &B; dir D,*dp = &D; base *p = &D; bp –>print(); // base dp –>print(); // dir p –>print(); // dir } Таким чином інтерпретація кожного виклику віртуальної функції через покажчик на базовий клас залежить від значення цього покажчика, тобто від типу об'єкта, для якого виконується виклик. Вибір того, яку віртуальну функцію викликати буде залежати від типу об'єкта, на який фактично (у момент виконання програми) спрямований покажчик, а не від типу покажчика. Віртуальними можуть бути тільки нестатичні функції-члени. Віртуальність успадковується. Після того як функція визначена як віртуальна, її повторне визначення в похідному класі (з тим же самим прототипом) створює в цьому класі нову віртуальну функцію, причому специфікатор virtual може не використовуватися. Конструктори не можуть бути віртуальними, на відміну від деструкторів. Практично кожен клас, що має віртуальну функцію, повинний мати віртуальний деструктор. Абстрактні класи Абстрактним називається клас, у якому є хоча б одна чиста (порожня) віртуальна функція. Чистою віртуальною функцією називається компонентна функція, що має наступне визначення: virtual тип ім'я_функції(список_формальних_параметрів) = 0; Чиста віртуальна функція нічого не робить і недоступна для викликів. Її призначення – бути основою для підмінюючих її функцій у похідних класах. Абстрактний клас може використовуватися тільки в якості базового для похідних класів. Механізм абстрактних класів розроблений для представлення загальних понять, що надалі передбачається конкретизувати. При цьому побудова ієрархії класів виконується за наступною схемою. На чолі ієрархії стоїть абстрактний базовий клас. Він використовується для спадкування інтерфейсу. Похідні класи будуть конкретизувати і реалізувати цей інтерфейс. В абстрактному класі оголошені чисті віртуальні функції, що по суті є абстрактними методи. Приклад . class Base{ public: Base(); // конструктор за замовчуванням Base(const Base&); // конструктор копіювання virtual ~Base(); // віртуальний деструктор virtual void Show()=0; // чиста віртуальна функція // інші чисті віртуальні функції protected: // захищені члени класу private: // часто залишається порожнім, інакше буде заважати майбутнім розробкам }; class Derived: virtual public Base{ public: Derived(); // конструктор за замовчуванням Derived(const Derived&); // конструктор копіювання Derived(параметри); // конструктор з параметрами virtual ~Derived(); // віртуальний деструктор void Show(); // перевизначена віртуальна функція // інші перевизначені віртуальні функції // інші перевантажені операції protected: // використовується замість private, якщо очікується спадкування private: // використовується для деталей реалізації }; Об'єкт абстрактного класу не може бути формальним параметром функції, однак формальним параметром може бути покажчик на абстрактний клас. У цьому випадку з'являється можливість передавати у функцію, що викликається, як фактичний параметр значення покажчика на похідний об'єкт, замінюючи ним покажчик на абстрактний базовий клас. У такий спосіб ми одержуємо поліморфні об'єкти. Абстрактний метод може розглядатися як узагальнення перевизначення. В обох випадках поводження батьківського класу змінюється для нащадка. Для абстрактного методу, однак, поводження просто не визначене. Будь-яке поводження задається в похідному класі. Одна з переваг абстрактного методу є чисто концептуальною: програміст може думкою наділити потрібною дією абстракцію як завгодно високого рівня. Наприклад, для геометричних фігур ми можемо визначити метод Draw, що їх малює: трикутник TTriangle, окружність TCircle, квадрат TSquare. Ми визначимо аналогічний метод і для абстрактного батьківського класу TGraphObject. Однак, такий метод не може виконувати корисну роботу, оскільки в класі TGraphObject просто немає достатньої інформації для малювання чого-небудь. Проте присутність методу Draw дозволяє зв'язати функціональність (малювання) тільки один раз із класом TGraphObject, а не вводити три незалежні концепції для підкласів TTriangle, TCircle, TSquare. Є і друга, більш актуальна причина використання абстрактного методу. В об’єктно-орієнтованих мовах програмування зі статичними типами даних, до яких відноситься і С++, програміст може викликати метод класу, тільки якщо компілятор може визначити, що клас дійсно має такий метод. Припустимо, що програміст хоче визначити поліморфну змінну типу TGraphObject, що буде в різні моменти часу містити фігури різного типу. Це припустимо для поліморфних об'єктів. Проте компілятор дозволить використовувати метод Draw для перемінної, тільки якщо він зможе гарантувати, що в класі змінної є цей метод. Приєднання методу Draw до класу TGraphObject забезпечує таку гарантію, навіть якщо метод Draw для класу TGraphObject ніколи не виконується. Природно для того, щоб кожна фігура малювалася по своєму, метод Draw повинний бути віртуальним.
©2015 arhivinfo.ru Все права принадлежат авторам размещенных материалов.
|