Подтвердить что ты не робот

Почему виртуальные базовые классы должны быть построены с помощью самого производного класса?

Следующий код не будет компилироваться:

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

class B: virtual public A {
public:
    B(): A(0) {}
};

// most derived class
class C: public B {
public:
    C() {} // wrong!!!
};

Если я вызываю A конструктор в C список инициализации конструктора, то есть:

// most derived class
class C: public B {
public:
    C(): A(0) {} // OK!!!
};

он работает.

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

Я не понимаю причину этого ограничения.

4b9b3361

Ответ 1

Потому что он избегает этого:

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

class B0: virtual public A {
public:
    B0(): A(0) {}
};

class B1: virtual public A {
public:
    B1(): A(1) {}
};

class C: public B0, public B1 {
public:
    C() {} // How is A constructed? A(0) from B0 or A(1) from B1?
};

Ответ 2

Поскольку в иерархии классов, имеющей фактически унаследованный базовый класс, базовый класс будет/может делиться несколькими классами (например, в наследовании алмаза, где один и тот же базовый класс унаследован несколькими классами). Это означает, что будет только одна копия фактически унаследованного базового класса. По сути, это означает, что базовый класс должен быть построен первым. В конечном итоге это означает, что производный класс должен создать экземпляр заданного базового класса.

Например:

class A;
class B1 : virtual A;
class B2 : virtual A;
class C: B1,B2 // A is shared, and would have one copy only.

Ответ 3

Я нахожу это правило ошибочным и громоздким (но тогда какая часть множественного наследования не является?).

Но логически наложенный порядок построения должен отличаться от случая нормального (не виртуального) наследования. Рассмотрим пример Ajay, минус virtual:

class A;
class B1 : A;
class B2 : A;
class C: B1,B2

В этом случае для каждого C два As строятся, один как часть конструкции B1, другой - как часть конструкции B2. Код класса B отвечает за это и может это сделать. Порядок событий:

Start C ctor
   Start B1 ctor
      A ctor (in B ctor code)
   End B1 ctor
   Start B2 ctor
      A ctor (in B ctor code)
   End B2 ctor
End C ctor

Теперь рассмотрим виртуальное наследование в

class A;
class B1 : virtual A;
class B2 : virtual A;
class C: B1,B2 

Один порядок события

Start C ctor
   Start B1 ctor
      // NO A ctor
   End B1 ctor
   Start B2 ctor
      // NO A ctor
   End B2 ctor

   A ctor // not B code!
End C ctor

Возможно, что A построено до B1 и B2, но это не имеет значения. Важно то, что конструкция A не происходит при построении других базовых классов. Этот код просто не выполняется и не может быть выполнен, потому что фактически унаследованный под-объект базового класса типа A является частью и под управлением самого производного класса, который недоступен из кода B1 и B2; действительно, C даже не полностью сконструирован в момент времени B1 или B2, и он может попытаться создать A в C.