2017-01-20 4 views
2

Я думаю, что пример будет описывать мою проблему лучше.Определение конструктора с виртуальным множественным наследованием

struct Base { 
    Base() = delete; 
    Base(int x) : x(x) {} 
    int x; 
}; 

struct Derived1 : virtual Base { 
    Derived1(int y) : Base(5), y(y) {} 
    int y; 
}; 

struct Derived2 : virtual Base { 
    Derived2(int z) : Base(10), z(z) {} 
    int z; 
}; 

struct Derived3: Derived1, Derived2 { 
public: 
    Derived3(int y, int z) : Derived1(y), Derived2(z) {} 

}; 

И ошибка я получаю: In constructor ‘Derived3::Derived3(int, int)’: error: use of deleted function ‘Base::Base()’ Derived3(int y, int z) : Derived1(y), Derived2(z) {}

Я не понимаю, почему эта ошибка возникает. На мой взгляд, все базовые классы фактически инициализируются в этом примере через их конструкторы (явно Derived1 и Derived2 и неявно Base - Derived2 (я не уверен здесь, может быть, через Derived1)). Хорошо, давайте сделаем то, что говорит мне компилятор.

struct Derived3: Derived1, Derived2 { 
public: 
    Derived3(int y, int z) : Base(-1), Derived1(y), Derived2(z) {} 

}; 

Он компилирует и если я сейчас это:

Derived3 d = Derived3(7, 9); 
std::cout << d.x << std::endl; 
std::cout << d.y << std::endl; 
std::cout << d.z << std::endl; 

я -1, 7, 9. И это совсем не то, чего я хотел. Идея состояла в том, чтобы инициализировать базовый класс с одним из его производных классов, и я ожидал, что первый номер быть 5 или 10.

Итак, вот мой вопрос: Почему я вынужден явно вызвать конструктор Base когда Он уже сделано в одном из производных классов?

Более конкретно, поскольку у меня есть множественное наследование и виртуальное наследование, я считаю, что любой экземпляр Derived3 имеет ровно одну копию экземпляра класса Base. И я ожидал, что эта копия Base инициализируется в наиболее производном классе его (Derived1 или Derived2), но, как я могу ясно видеть, что это не работает таким образом = (Где я ошибаюсь?

+0

Виртуальное наследование сложно. Ключ состоит в том, что виртуальный базовый подобъект «принадлежит» самому производному объекту, который также должен его инициализировать. –

+0

Вы правы в том, что 'Base' инициализируется в самом производном классе, но этот класс является' Derived3'. – molbdnilo

+0

@molbdnilo, тот похоже логичный. Может быть, вы знаете, как я могу сделать один из классов 'Derived1' и' Derived2' самым производным, поэтому мне не нужно будет вызывать 'Base (int)' в 'Derived3'? Я попытался создать более длинную цепочку подклассов, но это не сработало. – antonpp

ответ

4

Когда ты используйте виртуальное наследование, есть только 1 экземпляр базы. Кто должен инициализировать эту копию, Derived 1 или Derived 2? Нет способа узнать. Вот почему вы вынуждены делать это самостоятельно в Derived 3, поэтому не может быть двусмысленность. Именно поэтому вы получаете результат, который вы получаете вместо 5 или 10.

Без виртуального наследования как Derived 1, так и Derived 2 будут иметь свои собственные копии Base, за которые они будут нести ответственность, поэтому нет никакой двусмысленности Когда вы вынуждаете их наследовать от та же база Derived 3 должна взять на себя ответственность за Base, чтобы разрешить двусмысленности ... виртуальное наследование в лучшем случае странно.

+0

Да, спасибо, я понял. Но, возможно, есть какой-то способ, как я могу устранить неоднозначность и сообщить компилятору, какой из производных классов должен инициализировать базу? – antonpp

+0

@antonpp Не то, что я знаю, но я вряд ли являюсь экспертным пользователем для виртуального наследования, так что это не означает, что он не существует. Правда, хотя обычно, когда вы достигаете виртуального наследования, вы пытаетесь сделать плохую работу над дизайном. Есть некоторые законные варианты использования, но не многие. – RyanP

+0

@antonpp: Нет, потому что правило состоит в том, что «самый производный объект инициализирует виртуальную базу», а не «вы решаете, кто инициализирует виртуальную базу». –