【问题标题】:Casting of derived class to one of the bases through base pointer通过基指针将派生类强制转换为基数之一
【发布时间】:2013-09-18 00:12:45
【问题描述】:

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

我目前的层次结构有点类似于这样:

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)

【问题讨论】:

  • IDerived* d = (IDerived*)d; - 错字?第二个d 应该是derived 吗?
  • 谢谢,已经注意到并修复了;)
  • 另外,你不应该在 C++ 中使用 C 风格的强制转换,而应该使用 C++ 风格的强制转换,例如 static_castdynamic_cast
  • 看,这是上次编辑后的全新问题! :P
  • 是的,很抱歉我忽略了这里的角色模板。就像我说的,我讨厌所有那些离奇复杂的解决方案,在这种情况下我找不到更好的选择。

标签: c++ inheritance upcasting


【解决方案1】:

您正在执行交叉施法; IBaseIDerived 不相关。您需要先转换为Derived,然后再转换为IDerived。如果您使用 static_cast 而不是 C 转换,编译器会在编译时为您捕捉到这一点。

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

编辑:如果你不能使用 RTTI 并且你不知道对象的动态类型,那么虚拟继承和dynamic_cast 就消失了。当您调用 CreateDerived 时,看起来您确实知道对象的动态类型(因为它的模板参数),因此您可以拥有 std::map&lt;IBase*, IDerived*&gt;,然后在 CreateDerived&lt;TT, TB, TI...&gt;() 调用之后您可以 static_cast IBase*Derived&lt;TT, TB, TI...&gt;*,然后将指针作为键和值插入到映射中。

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

编辑2:或者,您似乎知道IBase* 指向的对象也确实派生自IDerived*。如果您可以修改类层次结构,您可以拥有一个派生自IBaseIDerived 的抽象基类,然后让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() {}};

【讨论】:

  • 谢谢你的回答,但这不是那么简单,我应该从一开始就澄清;)请看编辑
  • 感谢您的提示,我将研究 EDIT2 的建议,看看它是否在更大规模的场景中解决了问题;)我会告诉您
  • 从非常简短的测试看来,中间类解决了问题。尚不确定它将如何与样板的其余部分一起使用,但可以肯定它解决了所描述的问题,因此我将您的答案标记为解决方案。谢谢;)
猜你喜欢
  • 2013-09-23
  • 1970-01-01
  • 2023-03-27
  • 2016-12-31
  • 2014-03-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-10-10
相关资源
最近更新 更多