Как компилятор внутренне решает проблему с алмазом в C ++? - PullRequest
11 голосов
/ 12 сентября 2011

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

Например:

   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();
   }

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

Ответы [ 4 ]

4 голосов
/ 12 сентября 2011

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

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

3 голосов
/ 12 сентября 2011

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

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

1 голос
/ 12 сентября 2011

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

Liger[Tiger[Animal]Lion[Animal]]

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

Виртуальное наследование создаст такую ​​структуру, как

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

Теперь есть только одно Животное, доступное косвенно с обеих баз, поэтому переход от Лигера к Животному более неоднозначен.

1 голос
/ 12 сентября 2011

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...