Алмазное наследование и чисто виртуальные функции - PullRequest
7 голосов
/ 19 января 2009

Представьте себе стандартное наследование алмазов. Класс A определяет чисто виртуальную функцию fx, класс B определяет реализацию для fx, классы C и D ничего не делают с fx. При попытке вызвать fx на экземпляре класса D вы получите ошибку «неоднозначный вызов функции», хотя существует только одна реализация fx. Это может быть решено путем наследования B и C от A виртуальным способом. Это правильное решение проблемы? Как именно виртуальное наследование обрабатывает слияние таблиц виртуальных функций?

A ---> B ---> D

\ ---> C ------ ^

Ответы [ 2 ]

19 голосов
/ 19 января 2009

... Обратите внимание, Херб Саттер написал 3 отличные статьи о множественном наследовании (1) здесь , (2) здесь и (3) здесь . Он написал целую кучу полезных статей в "Гуру недели" здесь . Настоятельно рекомендуется ...

Во-первых, я не уверен, что правильно понял вашу иерархию. Я так понимаю, это так:

struct A {
    virtual void F() = 0;
};

struct B : A { void F() { } };
struct C : A { };
struct D : B, C { };

Что ж, D является абстрактным, потому что в объекте типа D есть два A подобъекта: один, который конкретизируется B через решетку B, и тот, который все еще является абстрактным в решетке, проходящей через C. Я так понимаю, у вас есть указатель на D и попробуйте позвонить F. Да, возникает неоднозначность, потому что компилятор находит две функции F в двух отдельных решетках:

D -> B::F
D -> C -> A::F

выглядит так:

    F()   F()
     A     A
     |     |
 F() B     C
      \   /
        D 

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

struct B : virtual A { void F() { } };
struct C : virtual A { };
struct D : B, C { };

У вас возникает такая ситуация, которая называется наследованием алмазов:

       F()  
        A
      /   \
 F() B     C
      \   /
        D 

И, выполняя поиск, он обнаруживает, что B::F переопределяет A::F. Хотя A::F все еще может быть достигнуто через D::C::A, это больше не двусмысленность, потому что A был унаследован виртуально.

Является ли это правильным решением в вашей конкретной проблеме - это, конечно, не ясно. Чаще всего есть лучшие способы, чем вывод виртуального из класса. На ваш вопрос о слиянии таблиц виртуальных функций - это полностью зависит от реализации. GCC, насколько я знаю, будет хранить указатель на один экземпляр A в виртуальной таблице D, если мы получим виртуальный.

3 голосов
/ 19 января 2009

Это правильное решение проблемы?

«Алмазное» наследование проблематично, и правильное объяснение решения требует некоторого объяснения. Я рекомендую вам прочитать следующие главы Meyers ' Effective C ++ :

  • Item 26, Защита от потенциальной неопределенности
  • Item 43, Разумное использование множественного наследования .
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...