Виртуальное наследование

Виртуальное наследование
Про наследование виртуальных методов, см виртуальный метод.

Виртуа́льное насле́дование (англ. virtual inheritance) в языке программирования C++ — один из вариантов наследования, который нужен для решения некоторых проблем, порождаемых наличием возможности множественного наследования (особенно «ромбовидного наследования»), путем разрешения неоднозначности того, методы которого из суперклассов (непосредственных классов-предков) необходимо использовать. Оно применяется в тех случаях, когда множественное наследование вместо предполагаемой полной композиции свойств классов-предков приводит к ограничению доступных наследуемых свойств вследствие неоднозначности. Базовый класс, наследуемый множественно, определяется виртуальным с помощью ключевого слова virtual.

Содержание

Суть проблемы

Рассмотрим следующую иерархию классов:

class Animal 
{
 public:
  virtual void eat();  // Метод определяется для данного класса
};
 
class Mammal : public Animal 
{
 public:
  virtual Color getHairColor();
 
 
 
};
 
class WingedAnimal : public Animal 
{
 public:
  virtual void flap();
 
};
 
// A bat is a winged mammal
class Bat : public Mammal, public WingedAnimal {};   //<--- обратите внимание, что метод eat() не переопределен в Bat
 
Bat bat;

Но как летучая мышь ест? Что происходит при вызове метода bat.eat()?


В вышеприведенном коде вызов bat.eat() является неоднозначным. Он может относиться как к Bat::WingedAnimal::Animal::eat() так и к Bat::Mammal::Animal::eat(). И у каждого наследника (WingedAnimal, Mammal) метод eat() определен по-своему. Проблема в том, что семантика традиционного множественного наследования не соответствует моделируемой им реальности. В некотором смысле, сущность Animal единственна по сути; Bat — это Mammal и WingedAnimal, но свойство животности (Animalness) летучей мыши (Bat), оно же свойство животности млекопитающего (Mammal) и оно же свойство животности WingedAnimal — по сути это одно и то же свойство.

Такая ситуация обычно именуется diamond inheritance («ромбическое наследование») и представляет из себя проблему, которую призвано решить виртуальное наследование.

Представление класса

Прежде чем продолжить, полезным будет рассмотреть, как классы представляются в C++. В частности, при наследовании классы предка и наследника просто помещаются в памяти друг за другом. Таким образом объект класса Bat это на самом деле последовательность объектов классов (Animal,Mammal,Animal,WingedAnimal,Bat), размещенных последовательно в памяти, при этом Animal повторяется дважды, что и приводит к неоднозначности.

Решение

Мы можем переопределить наши классы следующим образом:

class Animal 
{
 public:
  virtual void eat();
};
 
// Two classes virtually inheriting Animal:
class Mammal : public virtual Animal                // <--- обратите внимание на ключевое слово virtual
{
 public:
  virtual Color getHairColor();
};
 
class WingedAnimal : public virtual Animal          // <--- обратите внимание на ключевое слово virtual
{
 public:
  virtual void flap();
};
 
// A bat is still a winged mammal
class Bat : public Mammal, public WingedAnimal {};

Теперь, часть 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 будут все же разными, в отличие от располагаемой в них информации о смещениях.

Пример

Чтобы понять суть виртуального наследование без лишнего "шума", следует рассмотреть следующий пример:

#include <iostream>
 
class A
{
    public:
       int foo() {
           return 1;
       }
};
 
class B: public virtual A
{
    public:
};
 
class C : public virtual A
{
   public:
};
 
class D : public B, public C {};
 
int main()
{
    D d;
 
    std::cout << d.foo();
 
    return 0;
}

Если убрать ключевое слово virtual, то метод foo() не будет доступен как объект класса D и код не скомпилируется.

См. также

Литература

  • Подбельский В. В. Глава 10.2 Множественное наследование и виртуальные базовые классы // Язык Си++ / рец. Дадаев Ю. Г.. — 4. — М.: Финансы и статистика, 2003. — С. 336-359. — 560 с. — ISBN 5-279-02204-7, УДК 004.438Си(075.8) ББК 32.973.26-018 1я173

Wikimedia Foundation. 2010.

Игры ⚽ Нужен реферат?

Полезное


Смотреть что такое "Виртуальное наследование" в других словарях:

  • Наследование — это процесс передачи имущества (наследства, наследственного имущества) умершего к другим лицам. Наследование: Наследование (биология) Наследование (право) Наследование (программирование) Множественное наследование и его виды: Ромбовидное… …   Википедия

  • Наследование (программирование) — Для улучшения этой статьи желательно?: Найти и оформить в виде сносок ссылки на авторитетные источники, подтверждающие написанное. У этого термин …   Википедия

  • Множественное наследование — У этого термина существуют и другие значения, см. Наследование. Множественное наследование  свойство, поддерживаемое частью объектно ориентированных языков программирования, когда класс может иметь более одного суперкласса (непосредственного …   Википедия

  • Ромбовидное наследование — У этого термина существуют и другие значения, см. Наследование. Диаграмма наследования классов в виде ромба. Ромбовидное наследование ( …   Википедия

  • C++ — У этого термина существуют и другие значения, см. C. См. также: Си (язык программирования) C++ Семантика: мультипарадигмальный: объектно ориентированное, обобщённое, процедурное, метапрограммирование Тип исполнения: компилируемый Появился в …   Википедия

  • Виртуальность — В этой статье не хватает ссылок на источники информации. Информация должна быть проверяема, иначе она может быть поставлена под сомнение и удалена. Вы можете …   Википедия

  • Таблица виртуальных методов — (англ. virtual method table, VMT) координирующая таблица или vtable механизм, используемый в языках программирования для поддержки динамического соответствия (или метода позднего связывания). Допустим, программа содержит несколько классов в… …   Википедия

  • VMT — Таблица виртуальных методов (англ. virtual method table, VMT) координирующая таблица или vtable механизм, используемый в языках программирования для поддержки динамического соответствия (или метода позднего связывания). Допустим, программа… …   Википедия

  • Virtual function table — Таблица виртуальных методов (англ. virtual method table, VMT) координирующая таблица или vtable механизм, используемый в языках программирования для поддержки динамического соответствия (или метода позднего связывания). Допустим, программа… …   Википедия

  • Vtable — Таблица виртуальных методов (англ. virtual method table, VMT) координирующая таблица или vtable механизм, используемый в языках программирования для поддержки динамического соответствия (или метода позднего связывания). Допустим, программа… …   Википедия


Поделиться ссылкой на выделенное

Прямая ссылка:
Нажмите правой клавишей мыши и выберите «Копировать ссылку»