Здавалка
Главная | Обратная связь

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



До механізму віртуальних функцій звертаються в тих випадках, коли в кожному похідному класі потрібно свій варіант деякої компонентної функції. Класи, що включають такі функції, називаються поліморфними і відіграють особливу роль в ООП.

Віртуальні функції надають механізм пізнього (відкладеного) чи динамічного зв'язування. Будь-яка нестатична функція базового класу може бути зроблена віртуальною, для чого використовується ключове слово 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 повинний бути віртуальним.

9.1. Приклади програм Задача 9.1 Визначити клас "рядок" і перевантажити операцію + для з'єднання рядків. Використати різні категорії конструкторів та специфікації доступу до атрибутів. Об'єктно-орієнтований аналіз Об'єкт- це рядок. Атрибути (властивості) об'єкта такі: довжина рядка, символи рядка у вигляді масиву символів. Методи об'єкта такі: створення пустого рядка, ініціалізація атрибутів об'єкта, копіювання об'єкта, з'єднання об'єктів. Для створення рядків, що з'єднуються, використовується конструктор, який ініціалізує такі атрибути: ім'я об'єкта та символи рядка. Для створення об'єкта, який є результатом з'єднання рядків, використовується конструктор, який ініціалізує такі атрибути: ім'я об'єкта та довжину об'єднаного рядка. Для з'єднання двох об'єктів використовується операторна функція, яка виконує цю операцію за допомогою стандартної арифметичної операції додавання (+). Під час виконання операції додавання створюється новий об'єкт з атрибутами заданої довжини рядка, іменем об'єкта, який заповнений пустими символами. З'єднання рядків виконується стандартною функці-єю конкатенації. Під час повернення результату з функції з'єднання викликається конструктор копії, який створює новий об'єкт на основі копії вже існуючого. Під час повернення з функції посилання на об'єкт конструктор копії не викликається, тому що функція змінює значення фактичних об'єктів, а не їх копії. Для доступу до закритих атрибутів класу використовується функція доступу, яка повертає значення відповідного атрибута об'єкта. Об'єктно-орієнтоване програмування Структура проекту програми 9.1 Текст програми 9.1 //Файл lab9_1.h - інтерфейс класу classstroka { private: //закриті атрибути класу int len; //довжина рядка public: //відкриті атрибути та методи char name_str[10]; //ім’я об’єкта charstr[50]; //символи рядка stroka(char name[10]); //конструктор ініціалізації stroka (stroka& s); //конструктор копії stroka(char name[10],int size); //конструктор ініціалізації stroka operator+(stroka& s); //операторна функція перевантаження операції + int get(); //функція доступу до закритих атрибутів voidshow(); //відображення атрибутів об’єкта }; ////////////////////////////////////////////////////////////////////////////////////////////////////// //Файл lab9_1.cpp -реалізація класу #include<stdio.h> #include<iostream.h> #include<string.h> #include"lab9_1.h" //підключення заголовного файлу інтерфейсу класу ///////////////////// конструктор ініціалізації //////////////////////// stroka::stroka(charname[10]) { //викликається під час створення об’єктів, які будуть з’єднуватися strcpy(name_str,name); cout<<"Enter string "<<name_str<<endl; gets(str); len=strlen(str); } ///////////////////////// конструктор ініціалізації ///////////////////////// stroka::stroka(char name[10],int size) { //викликається під час створення об’єкта –результату з’єднання двох інших об’єктів strcpy(name_str,name); strcpy(str," "); len=size; } /////////////////////// відображення атрибутів об’єкта /////////////////// voidstroka::show() { cout<<"Initialization string "<<name_str<<endl; puts(str); } //////////////////////////// конструктор копії /////////////////////// stroka::stroka (stroka& s) /*викликається для копіювання результату з’єднання об’єктів в третій об’єкт під час повернення результату з операторної функції. Метод відсутній, якщо операторна функція повертає посилання на об’єкт типу stroka& */ { strcpy(name_str,s.name_str); strcpy(str,s.str); } //////////////////////////////////////// операторна функція з’єднання об’єктів///////////////////////////////// stroka stroka::operator+(stroka& s) { //можливий варіант повертання посилання на об’єкт stroka& stroka tmp("str3",len+s.len); //створення об’єкта, що повертається strcpy(tmp.str,str); strcat(tmp.str,s.str); //об’єднання рядків return tmp; //виклик конструктор копії } //////////////////////////////// функція доступу до закритих атрибутів ///////////////////////// intstroka::get() { returnlen; } ///////////////////////////// ТЕСТОВА ПРОГРАМА //////////////////////////////////// voidmain() { stroka a("str1"); //ініціалізація першого об’єкта значеннями з клавіатури a.show(); //відображення атрибутів першого об’єкта stroka b("str2"); //ініціалізація другого об’єкта значеннями з клавіатури b.show(); //відображення атрибутів другого об’єкта stroka c("str3",a.get()+b.get()); //ініціалізація об’єкта, який є з’єднанням об’єктів c=a+b; // з’єднання двох об’єктів c.show(); //відображення атрибутів результуючого об’єкта }     Задача 9.2 Розробити клас "матриця", який є похідним від класу "вектор". Клас "вектор" є похідним від класу "масив". Використати перевантажені констру-ктори. Виконати операції множення матриці на вектор та додавання матриць. Об'єктно-орієнтований аналіз
Ієрархія класів подана на рис. 9.1. Базовим класом є клас "масив" (array) з атрибутом - назва масиву (name), похідним від класу "масив" є клас "вектор" (vector) з атрибутами: кількість елементів (row), значення компонентів вектора (mas). Похідним від класу "вектор" є клас "матриця" (matrix) з атрибутами: кіль-кість стовпчиків матриці (column) та значення елементів матриці (matr). Кожний клас має конструктор об'єктів та метод show() відображення їх атрибутів. Клас "матриця" має методи множення матриці на вектор mult() та додавання матриць add(). Класи мають перевантажені конструктори, які використовуються під час ініціалізації об'єктів в різних умовах.






©2015 arhivinfo.ru Все права принадлежат авторам размещенных материалов.