Алмаз асимметричного виртуального наследования в C++

Итак, у меня есть эта идея, и я думаю, что ее практически невозможно реализовать на C++... но я хочу спросить. Я прочитал главу 15 Страуструпа и не получил ответа, и я не думаю, что миллиард других вопросов о бриллиантах, связанных с наследством, не отвечает на этот, поэтому я спрашиваю здесь.

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

class CommonBase { ... };

class BaseA : CommonBase { ... };

class BaseB : virtual CommonBase { ... };

class Derived : BaseA, BaseB { ... };

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

    LibBase
         | \
         |  \ 
         |   MyBase
         |     |
         |     |
 LibDerived    |
         | \   |
         |  \  |
         |   MyDerived
         |     |
LibDerived2    |
         | \   |
         |  \  |
         |   MyDerived2
         |     |
LibDerived3    |
         | \   |
         |  \  |
         |   MyDerived3
         |     |
LibConcrete    |
           \   |
            MyConcrete

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

class LibDerived : LibBase

Но если я заставлю свой класс виртуально наследовать

class MyBase : virtual LibBase {};
class MyDerived: virtual MyBase, virtual LibDerived {};

Так как MyDerived будет иметь виртуальную таблицу, а MyBase будет иметь виртуальную таблицу, будет ли только один объект LibBase?

Я надеюсь, что этот вопрос достаточно ясен.


person cheshirekow    schedule 07.08.2009    source источник
comment
С какой стати ты хочешь это сделать? :п   -  person jalf    schedule 08.08.2009
comment
Как я уже сказал, я хотел бы расширить функциональность библиотеки, создав класс, который переопределяет функцию-член класса в библиотеке, но при этом объект, наследуемый от этого класса, по-прежнему может использоваться библиотекой. Конкретный класс, который я хочу расширить, находится на 4 уровне наследования от базового класса, реализацию которого я хочу изменить. Я вижу, где мне может понадобиться повторно использовать эту функциональность и в других производных классах, поэтому простого переопределения необходимого метода в моем конкретном классе недостаточно.   -  person cheshirekow    schedule 10.08.2009


Ответы (2)


Чтобы упростить ответ, давайте подумаем о виртуальном/невиртуальном как о дублированном или недублированном контенте.

class LibDerived : LibBase

объявляет: я разрешаю LibBase дважды (или более) входить в нисходящий список LibDerived

class MyBase : virtual LibBase {};

объявляет: я разрешаю компилятору оптимизировать две записи LibBase в нисходящей MyBase в одну.

Когда эти два объявления встречаются друг с другом, первое имеет более высокий приоритет, поэтому MyDerived получает 2 реализации LibBase. Но сила c++ в том, чтобы решить эту проблему! Просто сделайте переопределение виртуальных функций MyDerived, чтобы выбрать, какие вы хотите использовать. Или другой способ - создать универсальную оболочку MyDerived, производную от интерфейса LibBase, которая агрегирует любой экземпляр: LibDerived, MyBase, ... и вызывает ожидаемый метод из агрегата.

person Dewfy    schedule 07.08.2009
comment
Можете ли вы объяснить резолюцию, которую вы упомянули? Когда вы говорите о переопределении виртуальных функций MyDerived для выбора тех, которые мне нужны, вы имеете в виду просто принять тот факт, что у вас есть два объекта LibBase, и переопределить методы, чтобы предоставить только один из них? К сожалению, я не думаю, что универсальная оболочка будет делать то, что мне нужно, потому что тогда она будет несовместима с остальной частью библиотеки. Я хотел бы, чтобы каждый из объектов производного класса выглядел так, как если бы он был собственным объектом библиотеки, но просто имел другую реализацию для некоторых методов. - person cheshirekow; 07.08.2009
comment
virtual в наследстве не позволяет компилятору оптимизировать базовые классы. Базовый класс virtual предназначен для гарантии того, что существует только один подобъект базового класса, независимо от того, сколько раз этот тип появляется в качестве виртуального базового класса в дереве наследования объекта производного класса. Не виртуальный базовый класс гарантированно добавит новый подобъект базового класса. Нет приоритета и нет возможности добавить вторую виртуальную базу для того же типа. - person CB Bailey; 07.08.2009
comment
Чеширеков, позвольте мне ответить в обратном порядке. Aggregate точно позволяет выбирать между несколькими реализациями, вы просто делаете оболочку вокруг реализации. И MyConcrete, например, во время конструктора, принимает указатель на ожидаемую реализацию и предоставляет ее, делегируя каждый вызов для агрегирования. примите тот факт, что у вас есть две LibBase - к сожалению, да, невиртуальное объявление предоставляет вам вручную переопределить каждую виртуальную функцию, чтобы принять решение, какой член двух копий должен быть вызван. - person Dewfy; 07.08.2009

По сути, вы правы. Вам нужно, чтобы LibDerived производилось виртуально из LibBase, если вы хотите, чтобы такое дерево наследования работало.

Если у вас его нет, вы не можете предотвратить наличие невиртуального LibBase под LibDerived и отдельного виртуального LibBase под MyBase.

person CB Bailey    schedule 07.08.2009
comment
Таким образом, каждая не виртуальная ссылка наследования в иерархии будет представлять новый базовый объект, а каждая виртуальная ссылка наследования будет сжата в один, другой, базовый объект? - person cheshirekow; 07.08.2009
comment
Точно. У вас есть один экземпляр виртуального базового класса для всех случаев, когда базовый класс появляется как виртуальная база в иерархии, плюс один экземпляр базового класса для каждого когда класс появляется как невиртуальный базовый класс в иерархии. - person CB Bailey; 07.08.2009