0

Я изучаю темы, связанные с множественным наследованием. Я придумал следующий код, и не мог полностью понять механизм позади него:Как работает полиморфизм с множественным наследованием?

struct root 
{ 
    virtual void vfunction(){ /* root version */ } 
}; 

struct mid1:public root 
{ 
    virtual void vfunction(){ /* mid1 version */ } 
}; 

struct mid2:public root 
{ 
    virtual void vfunction(){ /* mid2 version */ } 
}; 

struct inheritMulti:public mid1, public mid2 
{ 
    void ambiguityMethod(){ 
     vfunction(); // error: ambiguous 
    } 
    void method1(){ 
     mid1& t = *this; 
     t.vfunction(); 
    } 
    void method2(){ 
     mid2& t = *this; 
     t.vfunction(); 
    } 
}; 

ambiguityMethod ошибка, очевидно. Однако функция вызывает как method1, так и method2 смущает меня. Они также являются вызовом виртуальной функции, а t имеет тип inheritMulti. Поэтому они должны назвать inheritMulti версию vfunction, но поскольку inheritMulti не имеет собственной версии, я не знаю, что произойдет. И получается, что звонок в method1 вызывает версию mid1, а звонок в method2 вызывает версию mid2. Это что-то неопределенное и произошло только в моем компиляторе? Если нет, то почему это работает так? Как vtable справляется с такой ситуацией? Благодаря!


Спасибо, что помогли в первую очередь. Я сам искал связанные темы, так что да, я знаю «проблему с алмазами». Но я думаю, что мой вопрос отличается от этого. Моя основная проблема заключается в том, является ли поведение «виртуального вызова функции» в method1 и method2 четко определенным или неопределенным стандартом. В компиляторе он вел себя так, как я упоминал выше, но это поведение, обещанное стандартом? Если он четко определен, почему он вызывает mid1 и «mid2» версию соответственно? (Интуитивное мышление будет называть версию inheritMulti, так как тип t на самом деле inheritMulti) А также, как большинство компиляторов справляются с этой ситуацией? Странно, что вызов виртуальной функции в method1 и method2 вызывает различные функции. Еще раз спасибо!

+1

что вы думаете, что должно произойти здесь? – Creris

ответ

1
void method1(){ 
    mid1& t = *this; 
    t.vfunction(); 
} 

Здесь вы вызываете vfunction который не определен в inheritMulti, следовательно, он будет искать ближайшее определение vfunction, и что присутствует в mid1. См. Иерархию.

Root-> Mid1-> inheritMulti

Root-> Mid2-> inheritMulti

же в случае

`void method2(){ 
    mid2& t = *this; 
    t.vfunction(); 
} 

Здесь также ближайший определение было найдено в MID2 и, следовательно, вывод. Эти вызовы не являются двусмысленными, потому что обе структуры создали там собственную копию vfunction.

inheritMulti будет иметь два подобъекта с именем mid1 и mid2, и каждый из этих подобъектов сохраняет свой собственный указатель vtable. Хотя вы можете думать, что, когда вы делаете

mid1& t1 = *this; 

и mid2& t2 = *this;

как t1 и t2 же, но попытаться сделать это ...

bool same = ((void*)t1) == ((void*)t2); // Result false! 

Поскольку компилятор генерирует код позади, чтобы настроить указатель на соответствующий объект.

+0

Спасибо! Но остается еще один вопрос. Как работает vtable, если это связано с несколькими наследованиями? – Asu

+0

Обновлен мой ответ – Nishant

1

Виртуальное ключевое слово не играет здесь вообще.

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

void ambiguityMethod(){ 
     mid1::vfunction(); 
    } 

или

void ambiguityMethod(){ 
     mid2::vfunction(); 
    } 

или

void ambiguityMethod(){ 
     root::vfunction(); 
    } 

с т: (предупреждение: странный синтаксис впереди!)
t.mid1::vfunction();

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

void inheritMulti::vfunction() override { 
    return mid1::vfunction(); 
} 

компилятор будет искать ближайшую функцию с той же подписью при вызове vfunction, и будет вызывать эту функцию, которая, в свою очередь, - будет вызывать mid1 функция

1

Ваш код, описывающее «Проблема с алмазом» - хорошо известная проблема в множественном наследовании (для vfunction). Для method1 и method2 все в порядке, потому что вы делаете mid1 и mid2 объектами от this и звоните vfunction из их пространств имен.

+0

Не настоящий бриллиант, и не проблема. Вы можете быть двусмысленными, когда у вас есть MI или даже несколько 'using namespace' или с языком с импортом, когда у вас есть несколько импорта. – curiousguy

Смежные вопросы