Приведение производного класса к одной из баз через базовый указатель
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_castIBase*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() {}};