Програмування С, С++теорія та практика (частина 2)
2.7.2 Чисті віртуальні функції та абстрактні базові класи
У ситуаціях, коли віртуальні функції викликаються у похідному класі, але не визначаються у ньому, викликається функція базового класу. Однак є чимало випадків, коли просто не існує смислового визначення віртуальної функції у базовому класі.
Так, уявімо собі, що ми пишемо новий проект для зображення різних геометричних фігур на екрані і прагнемо спроектувати класи для таких спеціалізованих плоских фігур, таких як трикутник, прямокутник, коло тощо. Оскільки усі ці класи тісно взаємопов’язані, спочатку є сенс створити деякий базовий клас узагальненої фігури - Бкаре, що міститиме загальні атрибути будь-якої фігури.
Розглянемо ситуацію, коли однією з обов'язкових функцій в ієрархії буде віртуальна функція Агеа(), що друкує значення площі, яку займає фігура. Зрозуміло, що ми не можемо однозначно сказати, яке смислове значення цієї функції буде у базовому класі Браре, оскільки не можна визначити площу узагальненої фігури. З другого боку, кожний похідний клас в змозі визначити для себе власну версію цієї функції, що відповідає принципам поліморфізму в даній ситуації. Як тоді бути з визначенням такої функції у базовому класі?
Розв'язанням цієї проблеми є оголошення функції Агеа() як чистої віртуальної функції у базовому класі.
Чиста віртуальна функція (риге уігіиаі /ипсііоп)- це віртуальна функція, що не має визначення у базовому класі даної ієрархії. У такому випадку її тіло визначається, як чистий специфікатор, наприклад, як у нашому випадку :
^:сЬиа1 VОій. Агеа()=0;
// чиста віртуальна функція Агеа()
Такий опис нагадує звичайний прототип, що умовно прирівнюється нулю, який є ознакою чистої віртуальної функції. Якщо клас містить принаймні одну чисту віртуальну функцію, його часто іменують абстрактним класом (аЬзігасі сіазз). Його особливість така, що не можна створити екземпляр такого об'єкта, а лише покажчик або посилання на цей клас, тому що він містить невизначену віртуальну функцію. Загальна ідея використання абстрактних класів полягає у тому, щоб по мірі просування вниз по ієрархії класів останні набували більшої спеціалізації та ставали "менш абстрактними". Зрозуміло, що класи, які знаходяться у кінці лінії успадкування, повинні визначати усі абстрактні функції.
Отже, при оголошенні чистої віртуальної функції її поведінка не визначається: лише кожний похідний клас, що не є абстрактним, повинен визначати, що робитиме дана функція. Таким чином, зручним є використання абстрактного базового класу до проектування ієрархії у задачі, згаданої вище:
#іпс1игіе<іоз'Ьгеат.Ь> с1азз 8Ьаре {
рго'Ьес'Ьегі.: йоиЬ1е х,у; // змінні-координати точки іп£ со1;
//. . .
риЬііс:
Vоій. зеї( гіоиЬіе іх, гіоиЬіе іу=0)
{
х=іх ;
у=іу ;
}
^:сЬиаі Vоій. агеа()=0; // чиста віртуальна функція Агеа()
};
сіазз Тгіапдіе: риЬііс 8Ьаре {
риЬііс:
Vоій. агеа ()
{
сои^<<"Трикутник з висотою "<<х<<" та основою "<<у; сои^<<" має площу "<<0.5*х*у<<"\п";
}
};
сіазз КесЬапдіе: риЬііс 8Ьаре {
риЬііс:
Vоій. агеа ()
{
сои^<<"Прямокутник зі сторонами "<< х <<" та "<<у; сои^<<" має площу "<<х*у<<"\п";
}
};
сіазз Сігсіе: риЬііс 8Ьаре {
риЬііс:
Vоій. агеа ()
{
сои^<<"Коло з радіусом "<< х <<" має площу"
<<3.14159*х*х<<"\п";
}
};
таіп()
{
8Ьаре *р; // оголошення на базовий тип // (створити об'єкт не можна!
Тгіапдіе Т;
КесЬапдіе К;
Сігсіе С; р=&Т;
р->зе£(2,7); р->агеа() ; р=&К.;
р->зе£(4,6); р->агеа() ; р=&С ;
р->зе£(7); р->агеа() ;
}
Функція агеа(), яку можна обрахувати лише виходячи з опису самої фігури, визначається в усіх похідних класах з набуттям спеціалізації. Якщо цього не зробити у даній ієрархії, компілятор видасть повідомлення про помилку. Якщо доопрацювати цей проект у більш практичний бік, можна було б додати ще декілька віртуальних функцій, що також могли б бути оголошені, як чисті віртуальні:
^:сЬиа1 VОій. Йгам() ; // виведення фігури
^:сЬиа1 VОій. тоVе(іп^ гедіте) {....}
// переміщення фігури згідно режиму ^:сЬиа1 VОій. го'Ьа'Ье(іп'Ь і) {....}
// обертання фігури на заданий кут
Якщо у похідному класі, успадкованому від абстрактного базового, не відбулося заміщення чистої віртуальної функції, він також автоматично стане абстрактним. При подальшому успадкуванні рано чи пізно виникне потреба обов'язково визначити чисту віртуальну функцію саме у тому класі, який безпосередньо буде використовуватися для створення спеціалізованого об'єкту.
12 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
