Виртуальное наследование - база, используемая совместно с другими производными классами - PullRequest
0 голосов
/ 11 февраля 2012

Бьярне Страуструп пишет:

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

Я не понимаю этого утверждения.Какие возможные проблемы могут закрасться?

Чтобы объяснить это, он приводит странный пример

class A {                             // no constructor
    // ...
};
class B {
  public:
    B();                              // default constructor
    // ...
};
class C {
  public:
    C(int);                           // no default constructor
};
class D: virtual public A, virtual public B, virtual public C
{
    D() { /*... */ }                  // error: no default constructor for C
    D(int i) : C(i i) { /*... */ };   // ok
    // ...
};   

Это уместно здесь ??

Ответы [ 2 ]

2 голосов
/ 11 февраля 2012

Представьте, что у вас есть виртуальный базовый класс, который нужно инициализировать, вызвав .initialize(42), ровно один раз. Дело в том, что вы не знаете, какой производный класс должен его вызывать.

struct X : virtual A {
  X() { 
     // here 
  }
};

struct Y : virtual A {
  Y() { 
     // or here 
  }
};

struct Z : X, Y {
  // what about this    
};

Правильный ответ в этом случае - «иногда здесь, иногда там», но вы не знаете, что именно так (зависит от того, какой класс является наиболее производным подобъектом).

C ++ решает эту проблему для конструкторов, ул. конструкторы для всех виртуальных баз вызываются ровно один раз в конструкторе самого производного подобъекта; любой конструктор в иерархии должен быть готов вызвать виртуальный базовый конструктор (хотя во время выполнения это может не произойти).

1 голос
/ 11 февраля 2012

Представьте себе такую ​​ситуацию:

struct Base
{
    Base() { }
    virtual void call_me();
};

struct A : virtual Base { A() { call_me(); } };
struct B : virtual Base { B() { call_me(); } };

struct Derived : A, B
{
    Derived()
    :  Base()       // virtual base is construced in most-derived
    ,  A()
    ,  B()
    {  }
};

Теперь предупреждение о том, что промежуточные классы A и B могут предположить, что они единственные, которые вызывают Base::call_me(),Если бы их наследование Base было не виртуальным, это действительно было бы так, поскольку и A, и B имели бы свой собственный уникальный базовый класс.Однако с виртуальным наследованием не до A и B, чтобы определить, кто становится их базовым классом, поскольку только конечный класс Derived устанавливает фактический, единственный базовый класс, которыйявляется общим для всех посредников.Таким образом, в нашем примере и A, и B в итоге вызывают call_me() на одном и том же базовом объекте.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...