2011-09-12 8 views
11

Мы знаем, что мы можем решить проблему алмаза, используя виртуальное наследование.Как компилятор решает проблему алмаза в C++?

Например:

class Animal // base class 
    { 
    int weight; 
    public: 
    int getWeight() { return weight;}; 
    }; 
    class Tiger : public Animal { /* ... */ }; 
    class Lion : public Animal { /* ... */ }; 
    class Liger : public Tiger, public Lion { /* ... */ }; 
    int main() 
    { 
    Liger lg ; 
    /*COMPILE ERROR, the code below will not get past 
    any C++ compiler */ 
    int weight = lg.getWeight(); 
    } 

При компиляции этого кода мы получим ошибку неоднозначности. Теперь мой вопрос заключается в том, как компилятор внутренне обнаруживает эту проблему двусмысленности (проблема с алмазами).

+1

Обратите внимание: вы не используете виртуальное наследование в своем коде. Ваше описание, похоже, подразумевает, что пример касается решения проблемы алмаза с использованием виртуального наследования. –

+3

Я проголосовал за вас только потому, что «Лигер» заставил меня посмеяться :-) – selalerer

+1

Я всегда думал, что проблема с алмазом печатала алмаз на экран определенного размера ... взял меня на минуту, когда вы имели в виду конфликт наследования ... – corsiKa

ответ

4

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

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

3

Когда компилятор создает таблицу указателей функций для класса, каждый символ должен отображаться в нем ровно один раз. В этом примере getWeight появляется дважды: в Tiger и в Lion (потому что Liger не реализует его, поэтому он поднимается вверх по дереву, чтобы его искать), поэтому компилятор застревает.

Это довольно просто, на самом деле.

+0

Ошибка возникает только при вызове метода, а не при определении класса. Компилятору все равно, будет ли метод name + signature появляться дважды до тех пор, пока не будет вызова этого метода. – selalerer

+0

@sela, true, компилятор игнорирует методы, которые не вызываются. Компилятор создает таблицу только для используемых методов. Хороший компилятор, в любом случае, не знает, требуется ли это для каждой спецификации. – littleadv

+1

-1, компиляторы обычно не создают таблицы указателей функций. Особенно не «используются только методы». Это просто не связано с отдельной компиляцией; набор используемых методов обычно отличается на единицу перевода. Наиболее близкой является таблица экспорта в объектных файлах. Однако это не содержит запись 'Liger :: getWeight'. Так что это вообще не отвечает на вопрос. – MSalters

1

Компилятор ищет getWeight в Лигере, не находит никого, затем проверяет родителей и родителей своего родителя и т. Д., И если он находит больше одного, он возвращает ошибку и умирает от вас, потому что он не может скажите, какой из них он должен использовать.

+2

Только если символ найден более одного раза с одинаковым уровнем наследования, существует двусмысленность. Вообще говоря, двусмысленность может быть решена путем квалификации вызова; однако в частном случае проблемы с алмазом это не может быть, поскольку оба символа пришли из одного класса. –

+2

@MMavipc Возвращаемое значение не является частью сигнатуры метода. Компилятор не проверяет возвращаемое значение, когда он решает, какой метод вызывать. – selalerer

+0

@selalerer derp, спасибо, что указали это. – Avery3R

1

С вашим кодом, структурой для Liger является

Liger[Tiger[Animal]Lion[Animal]] 

Если вы вызываете Animal функцию от Liger указателя, есть фактически два животное Liger может преобразовать (отсюда неоднозначность)

Виртуальное наследование будет генерировать структуру как

Liger[Tiger[*]Lion[Animal]] 
      \-----/ 

Существует в настоящее время только один животное, косвенно добраться из обоих оснований, поэтому преобразование из Liger в Animal больше двусмысленно.

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

  • Нет связанных вопросов^_^