Подзадача Diamond: не множественное наследование в боковой ветви все еще требует конструктора класса - PullRequest
2 голосов
/ 18 июля 2010

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

  A
 / \* both virtual
B   C
 \ /
  D

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

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

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

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 изС когда коллирует с D каждый раз?Когда я пытаюсь добавить

using C::A

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

Ответы [ 2 ]

3 голосов
/ 03 ноября 2011

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

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

Это означает, что:

  • Что касается переопределения виртуальных функций, виртуальное наследование вообще не влияет на одиночное наследование;
  • но когда дело доходит до вызовов конструктора базового класса, виртуальное наследование влияет на каждый производный класс.
3 голосов
/ 18 июля 2010

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

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

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

...