Приведение производного класса к одной из баз через базовый указатель
EDIT: хорошо, как я вижу сейчас, это сильно меняет дело, поэтому более точный сценарий таков:
Иерархия, которую я в настоящее время имею, несколько похожа на эту:
class IBase() { virtual void Foo() = 0; };
class Base() : public IBase { virtual void Foo() { } };
class IDerived() { virtual void Bar() = 0; };
template<typename TT, typename TB, typename... TI>
class Derived : public Base, public IDerived { virtual void Bar() {}};
template<typename TT, typename TB, typename... TI>
IBase* CreateDerived() { return new Derived(); }
IBase* derived = CreateDerived<some types...>();
Я получаю ошибку с Visual Studio при попытке привести объект и вызвать функцию:
Run-Time Check Failure #0 - The value of ESP was not properly saved across a function call.
Все вызовы интерфейса IBase через derived работают нормально, но когда я пытаюсь привести derived к IDerived, я получаю ошибку вызова любой функции:
IDerived* d = (IDerived*)derived;
d->Bar(); <- boom error ;)
Я предполагаю, что такой бросок незаконен, но как я могу приведите указатель так, чтобы я мог получить доступ к методам интерфейса IDerived (предпочтительно без dynamic_cast, я бы предпочел хороший и портативный хак, если он существует ;))? Можно ли как-то вычислить смещение к указателю так, чтобы использовалась правильная vtable и все работало так, как должно ?
Я программирую довольно долго, но я всегда уклонялся от всех этих причудливых шуток над инженерными системами с тоннами интерфейсов и шаблонов, и теперь я обречен сделать один себя.
EDIT: теперь, как вы можете видеть, это становится сложным и трудным. Я не знаю точный тип производного, который я получаю, поскольку он шаблонен, также функция CreateDerived шаблонна и возвращает интерфейс.
Кроме того, одно из требований-не использовать dynamic_cast (RTTI отключен в проекте)
1 ответ:
Вы выполняете перекрестный бросок;
Я предполагаю, что вы знаете, чтоIBase
иIDerived
не связаны. Вам нужно сначала привести кDerived
, а затем кIDerived
. Если бы вы использовалиstatic_cast
, а не c-приведение, компилятор поймал бы это для вас во время компиляции.IBase
на самом деле являетсяDerived
, потому что вы использовали c-приведение, но если вы этого не сделаете, то вы также можете использоватьdynamic_cast
для безопасного выполнения перекрестных приведений.EDIT: если вы не можете использовать RTTI и не знаете динамический тип объекта, то виртуальный наследование и
dynamic_cast
уходят в окно. Когда вы вызываетеCreateDerived
, похоже, что вы знаете динамический тип объекта (из-за его аргументов шаблона), поэтому у вас может бытьstd::map<IBase*, IDerived*>
, а затем после вызоваCreateDerived<TT, TB, TI...>()
вы можетеstatic_cast
IBase*
toDerived<TT, TB, TI...>*
, а затем вставить указатель в качестве ключа и значения в карту.Просто включите RTTI; это становится сложным. >.
EDIT 2: альтернативно, вы, кажется, знаете, что объект, на который указывает
IBase*
, также является производным отIDerived*
. Если вы можете изменить иерархию классов, вы можете иметь абстрактный базовый класс, производный отIBase
иIDerived
, а затем иметьDerived
производный от этого нового базового класса. Затем вы можетеstatic_cast
изIBase*
в новый класс иерархии, а затем вIDerived*
.Это выглядело бы примерно так:
class IBase { virtual void Foo() = 0; }; class Base : public IBase { virtual void Foo() { } }; class IDerived { virtual void Bar() = 0; }; class BaseAndDerived : public Base, public IDerived { }; template<typename TT, typename TB, typename... TI> class Derived : public BaseAndDerived { virtual void Bar() {}};