3

编辑:好的,正如我现在看到的,这改变了很多情况,所以更精确的情况是这样的:

我目前拥有的层次结构与此有些相似:

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 接口的所有调用都可以正常工作,但是当我尝试将派生转换为 IDerived 时,调用任何函数时都会出现错误:

IDerived* d = (IDerived*)derived;
d->Bar(); <- boom error ;) 

我猜这样的转换是非法的,但是我怎样才能转换指针,以便我可以访问 IDerived 接口方法(最好没有 dynamic_cast,如果存在的话,我更喜欢一个好的和可移植的 hack ;))?是否有可能以某种方式计算指针的偏移量,以便使用正确的 vtable 并且一切正常?

我已经编程了很长时间,但我总是避开那些对具有大量界面和模板的工程系统的幻想,现在我注定要自己制作一个。

编辑:现在你可以看到这变得棘手和困难。我不知道我得到的 Derived 的确切类型,因为它是模板化的,函数 CreateDerived 也是模板化的并返回接口。

此外,其中一项要求是不使用 dynamic_cast(项目中禁用了 RTTI)

4

1 回答 1

1

您正在执行交叉演员;IBase并且IDerived是无关的。您需要先转换为Derived,然后再转换为IDerived。如果您使用 astatic_cast而不是 C 强制转换,编译器会在编译时为您捕捉到这一点。

我假设您知道这IBase确实是 a Derived,因为您使用了 C 转换,但如果您不这样做,那么您也可以使用它dynamic_cast来安全地执行交叉转换。

编辑:如果您不能使用 RTTI 并且您不知道对象的动态类型,那么虚拟继承并dynamic_cast走出窗口。当你调用CreateDerived它时,看起来你确实知道对象的动态类型(因为它的模板参数)所以你可以有一个std::map<IBase*, IDerived*>然后在CreateDerived<TT, TB, TI...>()调用之后你可以到static_cast然后将指针作为键和值插入到地图中.IBase*Derived<TT, TB, TI...>*

只需启用 RTTI;这变得越来越复杂。>.<

编辑 2:或者,您似乎知道 指向的对象IBase*也派生自IDerived*. 如果您可以修改类层次结构,您可以拥有一个派生自两者的抽象基类,IBase然后IDerived派生Derived自这个新的基类。然后您可以static_castIBase*到 层次结构中的新类,然后到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() {}};
于 2013-09-13T10:30:11.963 回答