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


54

У C ++ є багаторазове успадкування, багато мовних конструкцій забороняють це як небезпечне. Але деякі мови, такі як Ruby і PHP, використовують дивний синтаксис, щоб зробити те ж саме, і називають його mixins або traits. Я багато разів чув, що міксини / риси складніше зловживати, ніж звичайне багаторазове успадкування.

Що конкретно робить їх менш небезпечними? Чи є щось, що неможливо за допомогою комбінацій / ознак, але можливо з багатократним успадкуванням у стилі C ++? Чи можна зіткнутися з проблемою алмазів з ними?

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


7
З нетерпінням чекаю, що хтось опублікував добре написану, продуману та ґрунтовну відповідь на це.
Роберт Харві

7
Ruby та PHP не ввели міксин та ознак. Міксини були введені в "Ароматизатори" (1980), "Риси" були введені в "Скріп Малий корок" (2001). Ароматизатори були першою об'єктно-орієнтованою мовою з багаторазовим успадкуванням, і вона використовувала міксини. C ++ отримав лише багаторазове успадкування у версії 2.0, яка була випущена в 1989 році, через 9 років після Flavors. Отже, питання повинно бути таким: "У ароматах є звичайні міксини, але деякі мови, такі як C ++, вводять дивний синтаксис, щоб зробити те саме, і називають це багатократним успадкуванням.
Йорг W Міттаг

Відповіді:


31

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

Неоднозначність проявляється кількома різними способами:

  1. Якщо у вас є два базових класи з одним xі тим же полем , і похідний тип запитує x, що він отримує?
    • Якщо обидві xзмінні мають невідповідні типи, ви можете це зробити.
    • Якщо вони одного типу, ви можете спробувати об'єднати їх у одну змінну.
    • Ви завжди можете викрити їх як дивні цілком кваліфіковані імена.
  2. Якщо у вас є два базових класи з однаковою функцією fз однаковими підписами, і хтось дзвонить f, хто отримує виклик?
    • Що робити, якщо два базових класи поділяють іншого загального віртуального предка (проблема алмазів).
    • Що робити, якщо функція має різні, але сумісні підписи?
  3. Коли ви будуєте клас з двома базовими класами, який із конструкторів базових класів називається першим? Коли ви знищуєте об'єкт, який вбивають?
  4. Коли ви викладаєте об'єкт в пам'ять, як це робити послідовно?
  5. Як ви вирішуєте всі ці випадки за допомогою 3 базових класів? 10?

І це ігнорує такі речі, як динамічна диспетчеризація, умовивідведення типу, відповідність шаблонів та інші речі, про які я знаю менше, які стають складнішими, коли мова підтримує багатократне успадкування повних класів.

Риси чи мікшн (або інтерфейси, або ...) - це всі конструкції, які конкретно обмежують можливості типу, щоб не було двозначності. Вони рідко самі володіють чим-небудь. Це дозволяє складності типів проходити більш гладко, оскільки немає двох змінних або двох функцій ... є змінна та посилання; функція та підпис. Компілятор знає, що робити.

Інший поширений підхід полягає в тому, щоб змусити користувача "будувати" (або змішувати) свій тип по черзі. Замість того, щоб базові класи були рівними партнерами нового типу, ви додаєте один тип до іншого - переосмислюючи все, що там було (як правило, з необов'язковим синтаксисом для перейменування та / або повторного викриття перезаписаних бітів).

Чи є щось, що неможливо за допомогою комбінацій / ознак, але можливо з багатократним успадкуванням у стилі C ++?

Залежно від мови - зазвичай стає проблематичним або неможливим об'єднати реалізацію функцій та зберігання змінних з декількох базових класів та викрити їх у похідному типі.

Чи можна зіткнутися з алмазною проблемою з ними?

Іноді менш чіткі варіанти з’являться на основі вашої мови, але зазвичай ні. Вся суть рис полягає в тому, щоб порушити таку неоднозначність.


Чи можете ви надати мені приклад мови / технології, що дозволяють використовувати такі типи "побудови" для екземпляра?
Gherman

@german - Я, звичайно, не експерт Scala, але я розумію, що саме це withключове слово там.
Теластин

"конструкції, які конкретно обмежують можливості типу, щоб не було двозначності": які обмеження? Інтерфейси не мають змінних, тому це одна межа, але риси Scala та модулі Ruby. Чи обмежені вони іншими способами?
Девід Молес

@DavidMoles - це звичайний спосіб, я також бачив обмеження у правилах віртуальної диспетчеризації (думаю, що C # явно реалізував інтерфейси).
Теластин
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.