Что такое виртуальный базовый класс В C++?



Я хочу знать, что это "виртуальный базовый класс" и что это означает.



позвольте мне показать пример:



class Foo
{
public:
void DoSomething() { /* ... */ }
};

class Bar : public virtual Foo
{
public:
void DoSpecific() { /* ... */ }
};
844   10  
c++

10 ответов:

виртуальные базовые классы, используемые в виртуальном наследовании, - это способ предотвращения появления нескольких "экземпляров" данного класса в иерархии наследования при использовании множественного наследования.

рассмотрим следующий сценарий:

class A { public: void Foo() {} };
class B : public A {};
class C : public A {};
class D : public B, public C {};

вышеприведенная иерархия классов приводит к "страшному алмазу", который выглядит так:

  A
 / \
B   C
 \ /
  D

экземпляр D будет состоять из B, который включает A, и C, который также включает A. Таким образом, у вас есть два "экземпляра" (для не хватает лучшего выражения) А.

когда у вас есть этот сценарий, у вас есть возможность двусмысленности. Что происходит, когда вы делаете это:

D d;
d.Foo(); // is this B's Foo() or C's Foo() ??

виртуальное наследование есть, чтобы решить эту проблему. Когда вы указываете virtual при наследовании ваших классов, вы говорите компилятору, что вам нужен только один экземпляр.

class A { public: void Foo() {} };
class B : public virtual A {};
class C : public virtual A {};
class D : public B, public C {};

это означает, что существует только один "экземпляр" a, включенный в иерархию. Отсюда

D d;
d.Foo(); // no longer ambiguous

надеюсь, что помогает как мини-резюме. Для получения дополнительной информации прочтите этой и этой. Хороший пример также доступен здесь.

о макете памяти

как Примечание стороны, проблема с ужасным Алмазом заключается в том, что базовый класс присутствует несколько раз. Таким образом, с регулярным наследованием вы считаете, что у вас есть:

  A
 / \
B   C
 \ /
  D

но в макете памяти, у вас есть:

A   A
|   |
B   C
 \ /
  D

это объясняет, почему при вызове D::foo(), у вас есть проблема неоднозначности. Но это реальные проблема возникает, когда вы хотите использовать переменную-член A. Например, предположим, что мы есть:

class A
{
    public :
       foo() ;
       int m_iValue ;
} ;

когда вы попытаетесь получить доступ к m_iValue С D, компилятор будет протестовать, потому что в иерархии он увидит два m_iValue, а не один. И если вы измените один, скажем, B::m_iValue (то есть A::m_iValue родитель B),C::m_iValue не будет изменен (то есть A::m_iValue родитель C).

вот где виртуальное наследование пригодится, так как с ним вы вернетесь к истинному алмазному макету, а не только к одному foo() только метод, но и один и только один m_iValue.

что может пойти не так?

представьте себе:

  • A имеет некоторые основные функции.
  • B добавляет к нему какой-то классный массив данных (например)
  • C добавляет к нему некоторые интересные функции, такие как шаблон наблюдателя (например, на m_iValue).
  • D наследует от B и C, и, таким образом, с A.

С нормальным наследования, изменение m_iValue С D неоднозначно, и это должно быть решено. Даже если это так, есть два m_iValues внутри D, так что вам лучше запомнить это и обновить два одновременно.

с виртуальным наследованием, изменение m_iValue С D ОК... Но... Допустим, что у вас есть D. Через его C интерфейс, вы прикрепили наблюдателя. И через его B интерфейс, вы обновляете прохладный массив, который имеет побочный эффект непосредственного изменения m_iValue...

смена m_iValue делается напрямую (без использования метода виртуального доступа), наблюдатель "слушает" через C не будет вызван, потому что код, реализующий прослушивание, находится в C и B об этом не знает...

вывод

если у вас есть алмаз в вашей иерархии, это означает, что у вас есть 95%, чтобы сделать что-то не так с указанной иерархией.

объяснение множественного наследования с виртуальными базами требует знания объектной модели C++. И объяснение темы четко лучше всего делать в статье, а не в поле для комментариев.

лучшим, читаемым объяснением, которое я нашел, что решило все мои сомнения по этому вопросу, была эта статья:http://www.phpcompiler.org/articles/virtualinheritance.html

вам действительно не нужно будет читать что-либо еще по этой теме (Если вы не являетесь автором компилятора) после прочтения этого...

виртуальный базовый класс-это класс, который нельзя инстанцировать нельзя создайте из него прямой объект.

Я думаю, что вы путаете две очень разные вещи. Виртуальное наследование-это не то же самое, что абстрактный класс. Виртуальное наследование изменяет поведение вызовов функций; иногда оно разрешает вызовы функций, которые в противном случае были бы неоднозначными, иногда оно переносит обработку вызовов функций в класс, отличный от ожидаемого в невиртуальном наследовании.

Я хотел бы добавить к добрым разъяснениям OJ.

виртуальное наследование не приходит без цены. Как и со всеми виртуальными вещами, вы получаете хит производительности. Есть способ обойти этот хит производительности, который, возможно, менее элегантен.

вместо того, чтобы разбить Алмаз путем получения практически, Вы можете добавить еще один слой к алмазу, чтобы получить что-то вроде этого:

   B
  / \
D11 D12
 |   |
D21 D22
 \   /
  DD

ни один из классов не наследует виртуально, все наследуют публично. Классы D21 и D22 затем скроет виртуальную функцию f (), которая неоднозначна для DD, возможно, объявив функцию частной. Каждый из них определял бы функцию-оболочку, F1() и f2() соответственно, каждый вызывающий класс-локальный (частный) f(), таким образом разрешая конфликты. Класс DD вызывает f1 (), если он хочет D11::f() и f2 (), если он хочет D12::f (). Если вы определяете встроенные обертки, вы, вероятно, получите около нуля накладных расходов.

конечно, если вы можете изменить D11 и D12, то вы можете сделать тот же трюк внутри этих классов, но часто это не так.

в дополнение к тому, что уже было сказано о множественном и виртуальном наследовании, есть очень интересная статья в журнале доктора Добба:Множественное Наследование Считается Полезным

ты немного путаешь. Я не знаю, если вы путаете некоторые понятия.

У вас нет виртуального базового класса в ОП. Вы просто иметь базовый класс.

вы сделали виртуальное наследование. Это обычно используется в множественном наследовании, так что несколько производных классов используют члены базового класса, не воспроизводя их.

базовый класс с чистой виртуальной функцией не создается. для этого требуется синтаксис, который получает Павел. Оно обычно используется так, что производные классы должны определить эти функции.

Я не хочу больше ничего объяснять, потому что я не совсем понимаю, о чем вы просите.

Это означает, что вызов виртуальной функции будет направлен в "правильный" класс.

C++ FAQ Lite FTW.

короче говоря, он часто используется в сценариях множественного наследования, где формируется" Алмазная " иерархия. Виртуальное наследование затем нарушит двусмысленность, созданную в нижнем классе, когда вы вызываете функцию в этом классе, и функция должна быть разрешена либо в классе D1, либо в D2 выше этого нижнего класса. Смотрите FAQ item для диаграммы и деталей.

Он также используется в делегация сестра, мощная функция (хотя и не для слабонервных). Смотрите этой FAQ.

Также см. пункт 40 в действующем 3-м издании C++ (43 во 2-м издании).

Алмаз наследование запускаемый пример использования

в этом примере показано, как использовать виртуальный базовый класс в типичном сценарии: для решения проблемы наследования алмазов.

#include <cassert>

class A {
    public:
        A(){}
        A(int i) : i(i) {}
        int i;
        virtual int f() = 0;
        virtual int g() = 0;
        virtual int h() = 0;
};

class B : public virtual A {
    public:
        B(int j) : j(j) {}
        int j;
        virtual int f() { return this->i + this->j; }
};

class C : public virtual A {
    public:
        C(int k) : k(k) {}
        int k;
        virtual int g() { return this->i + this->k; }
};

class D : public B, public C {
    public:
        D(int i, int j, int k) : A(i), B(j), C(k) {}
        virtual int h() { return this->i + this->j + this->k; }
};

int main() {
    D d = D(1, 2, 4);
    assert(d.f() == 3);
    assert(d.g() == 5);
    assert(d.h() == 7);
}

виртуальные классы не то же, что и виртуальное наследование. Виртуальные классы вы не можете создать экземпляр, виртуальное наследование - это совсем другое.

Википедия описывает это лучше, чем я могу. http://en.wikipedia.org/wiki/Virtual_inheritance

Comments

    Ничего не найдено.