Віртуальне успадкування
Матеріал з Seo Wiki - Пошукова Оптимізація та Програмування
У мові програмування C++,віртуальне успадкування(англ.virtual inheritance) це один з варіантів успадкування, який потрібен для вирішення деяких проблем, що породжуються наявністю можливості множинного успадкування (особливо «проблемою ромба») «diamond problem»)) шляхом вирішення неоднозначності того, методи якого з класів-предків необхідно використовувати. Воно застосовується у тих випадках, коли множинне успадкування замість передбачуваної повної композиції властивостей класів-предків призводить до обмеження доступних успадкованих властивостей внаслідок неоднозначності. Базовий клас, успадкований множиною, визначається віртуальним з допомогою ключового слова virtual .
Зміст
Суть проблеми
Розглянемо таку ієрархію класів.
Але як кажан їсть? Що відбувається під час виклику методу bat.eat()?
У наведеному вище коді виклик bat.eat() є неоднозначним. Він може ставитися як до Bat::WingedAnimal::Animal::eat() так і до Bat::Mammal::Animal::eat() . Проблема в тому, що семантика традиційного множинного успадкування не відповідає реальності, що моделюється ним. У певному сенсі, сутність Animal єдина насправді; Bat це Mammal і WingedAnimal, але властивість тварини (Animal ness) кажана (Bat), воно ж властивість тварини ссавця (Mammal) і воно ж властивість тварини WingedAnimal - по суті це те саме властивість.
Така ситуація зазвичай іменуєтьсяdiamond inheritance(«ромбічне успадкування») і є проблемою, яку покликане вирішити віртуальне успадкування.
Подання класу
Перш ніж продовжити, корисним буде розглянути як класипредставляються у C++. Зокрема, при успадкування класи предка і спадкоємця просто поміщаються у пам'яті один за одним. Таким чином об'єкт класу Bat це насправді послідовність об'єктів класів (Animal,Mammal,Animal,WingedAnimal,Bat), розміщених послідовно в пам'яті, при цьому Animal повторюється двічі, що призводить до неоднозначності.
Ми можемо перевизначити наші класи так:
Тепер частина Animal об'єкта класу Bat::WingedAnimalта самащо і частина Animal , яка використовується в Bat::Mammal , і можна сказати, що Bat має у своєму поданні тільки одну частину Animal і виклик Bat:: eat() стає однозначним.
Віртуальне успадкування реалізується через додавання покажчиків на віртуальну таблицю vtable до класів Mammal і WingedAnimal , це робиться зокрема оскільки зміщення пам'яті між початком Mammal та її Animal частини невідомо на етапі компіляції, а з'ясовується лише під час виконання. Таким чином Bat представляється як (vtable*, Mammal, vtable*, WingedAnimal, Bat, Animal). Два вказівники vtable на об'єкт збільшують розмір об'єкта на величину двох покажчиків, але це забезпечує єдиність Animal і відсутність багатозначності. Потрібні два покажчики vtables: по одному на кожного предка в ієрархії, який віртуально успадковується від Animal: один для Mammal і один для WingedAnimal. Усі об'єкти класу Bat матимуть одні й самі покажчики vtable *, але кожен окремий об'єкт Bat міститиме власну реалізацію об'єкта Animal. Якщо якийсь інший клас буде успадковуватися від Mammal, наприклад Squirrel, то vtable* в об'єкті Mammal об'єкта Squirrel відрізнятиметься від vtable* в об'єкті Mammal об'єкта Bat, хоча в особливому випадку вони, як і раніше, можуть бути однакові по суті: коли частинаSquirrel об'єкта має той самий розмір, що і частина Bat, оскільки тоді відстань від реалізації Mammal до частини Animal буде однаковою. Але самі віртуальні таблиці vtables будуть все ж таки різними, на відміну від наявної в них інформації про усунення.