2009-08-07 4 views
4

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

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

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» был be объект класса, который они существенно заменяют, но я хочу, чтобы следующий класс на диаграмме наследования использовал реализацию переопределенного метода из «My» базового класса, но все другие методы из классов библиотеки. Классы библиотеки не наследуют практически так же, как этот

class LibDerived : LibBase 

Но если я мой класс наследует практически

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

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

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

+2

Почему бы вам не сделать это?: p – jalf

+0

Как я уже сказал, я хотел бы расширить функциональность библиотеки, создав класс aa, который переопределяет функцию-член класса в библиотеке, но все же объект, который наследует от этого класса, все еще может использоваться библиотекой , Конкретный класс, который я хочу расширить, - это 4 уровня наследования от базового класса, который я хочу изменить. Я вижу, где мне, возможно, придется повторно использовать эту функциональность и в других производных классах, поэтому просто переопределить необходимый метод в моем конкретном классе недостаточно. – cheshirekow

ответ

2

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

class LibDerived : LibBase 

заявляет: Я разрешаю LibBase быть в два раза (или больше) вступил в Нисхождение LibDerived

class MyBase : virtual LibBase {}; 

заявляет: Я позволяю компилятору оптимизировать две записи из LibBase в MyBase descendings в один.

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

+0

Можете ли вы подробнее объяснить упомянутое решение? Когда вы говорите, что переопределяете виртуальные функции MyDerived, чтобы выбрать те, которые я хочу, вы имеете в виду просто «принять тот факт, что у вас есть два объекта LibBase и переопределить методы, чтобы выставить только один из них»? К сожалению, я не думаю, что универсальная оболочка будет делать то, что мне хотелось бы, потому что она тогда не будет совместима с остальной библиотекой. Я хотел бы, чтобы каждый из объектов производного класса «появлялся», как если бы он был родным объектом библиотеки, но просто использовал другую реализацию для некоторых методов. – cheshirekow

+1

'virtual' в heritance не позволяет компилятору оптимизировать базовые классы. Базовый класс 'virtual' предназначен для того, чтобы гарантировать, что существует только один подкомпонент базового класса независимо от того, сколько раз этот тип появляется как виртуальный базовый класс в дереве наследования объекта производного класса. Невиртуальному базовому классу гарантированно будет добавлен новый под-объект базового класса. Нет приоритета, и нет возможности добавить вторую виртуальную базу для того же типа. –

+0

Чеширеков, позвольте мне ответить в порядке. Агрегат точно позволяет выбирать между несколькими реализациями, вы просто делаете обертку вокруг имплантата. И MyConcrete, например, во время конструктора, принимает указатель на ожидаемую реализацию и раскрывает его, обрабатывая каждый вызов для аггрерации. «Примите тот факт, что у вас есть две LibBase» - к сожалению, «да», но не виртуальная декларация позволяет вам вручную переопределить каждую виртуальную функцию, чтобы принять решение о том, какой член из двух копий должен быть вызван. – Dewfy

2

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

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

+0

Таким образом, каждая невиртуальная ссылка на наследование в иерархии вводит новый базовый объект, и каждая виртуальная ссылка на наследство будет уплотнена в один, другой, базовый объект? – cheshirekow

+2

Точно. У вас есть один экземпляр виртуального базового класса для _all_, когда базовый класс отображается в виде виртуальной базы в иерархии * плюс * один экземпляр базового класса для _each_ time класс отображается как не виртуальный базовый класс в иерархии. –