Алмазная подпроблема: немножественное наследование в побочной ветви по-прежнему требует конструктора класса

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

  A
 / \* both virtual
B   C
 \ /
  D

Однако мой базовый класс A не имеет конструктора по умолчанию, поэтому я должен был вызвать его вручную из D. Однако, когда я пытаюсь добавить класс E в этот бриллиант как C-унаследованный

  A
 / \* both virtual
B   C
 \ / \
  D   E

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

class A                     
   {public: 
      A (int _N): N(_N) {};
      void show()
        {cout<<"A"<<N;} 
    protected:
      int N;
   }; 
class B: public virtual A   
   { public: 
       B(int n): A(2*n) {};
       void show()
        { cout<<"B"<<N;} 
   }; 
class C: public virtual A   
   { public: 
       C(int n): A(3*n) {};
       void show()
        { cout<<"C"<<N;} 
   }; 
class D: public B,C 
   { public: 
       D(): B(1), C(2), A(3) {};
       void show()
        { cout<<"D"<<N;} 
   }; 

class E: public virtual C
   { public:
       E(): C(1) {};
       void show()
        { cout<<"E"<<N;} 
   }; 

int main()
  {D d;       // OK
   A *a = &d; 
   a->show(); 

   E e;        // NOT OK, no function A::A() to call in E::E()
   A *a2 = &e;
   a2->show();
   return 0;
  } 

Можно ли решить эту проблему без вызова конструктора A из E? Мне нужен C, чтобы сделать это правильно :-).

Или можно вообще не пытаться решить алмазную проблему:

A   A
|   |  no virtual at all
B   C
 \ / \
  D   E

и по-прежнему пытаться объявить объект класса D с двумя экземплярами A, но указывать компилятору использовать A из C при каждом сборе из D? Когда я пытаюсь добавить

using C::A

в объявление D все равно выдает ошибку однозначной базы A.


person Nick    schedule 18.07.2010    source источник


Ответы (2)


Можно ли решить эту проблему без вызова конструктора A из E? Мне нужен C, чтобы сделать это правильно :-).

Конструктор для наиболее производного класса (в данном случае E) отвечает за вызов конструктора для любых виртуальных базовых классов.

Конструктор C не может вызывать конструктор A, потому что C не является самым производным классом. Виртуальные базовые классы инициализируются перед любыми прямыми базовыми классами, поэтому E должен инициализироваться A, прежде чем он сможет инициализировать C.

person James McNellis    schedule 18.07.2010
comment
Спасибо, это понятно, так это точное значение виртуального наследования? Теперь я вижу. Наверное, виртуальное наследование здесь мне не нужно, к сожалению, это будет не удобно (поскольку производных от Е много и каждая должна вызывать конструктор А так же, как С и т.д.). Может быть, есть способ обойти проблему с бриллиантами без виртуального наследования (подобно использованию)? - person Nick; 18.07.2010
comment
@Nick: Трудно сказать на таком абстрактном примере. Честно говоря, я обычно избегаю сложных иерархий наследования в коде, который пишу. У кого-то может быть хорошее прагматичное решение для общего случая. - person James McNellis; 18.07.2010
comment
Я подумал... ну, в моем конкретном случае мне не нужно, чтобы C был потомком A. Вместо этого E может быть прямым потомком A (вместе с C). @Джеймс, еще раз большое спасибо. - person Nick; 19.07.2010

Да, вызовы конструктора виртуального базового класса отличаются от переопределения виртуальных функций:

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

Это подразумевает, что:

  • что касается переопределения виртуальных функций, виртуальное наследование вообще не влияет на одиночное наследование;
  • но когда дело доходит до вызовов конструктора базового класса, виртуальное наследование влияет на каждый производный класс.
person curiousguy    schedule 02.11.2011