在什么情况/情况下,dynamic_cast<>会失败吗?

时间:2012-02-11 07:55:39

标签: c++ dynamic-cast dynamic-typing

在修复巨大代码库中的错误时,我观察到一种奇怪的情况,即引用的动态类型从原始Derived类型更改为Base类型!我提供了解释问题的最小代码:

struct Base {
  // some 'virtual' function
  protected: // copy constructor
  private:  // assignment operator
};

struct Derived : Base {
  ... // There are few more classes between `Base` and `Derived`
  ... // but for simplicity, I have put direct relation
};

void foo (Base &ref)
{
  SomeClass obj;
  obj.pVoid = &ref;  // pVoid is of void*

  // ----> typeid(ref) = Derived
  (*funcptr)(obj);
  // ----> typeid(ref) = Base !!!

  Derived *p = dynamic_cast<Derived*>(&ref);  // this fails ... i.e. "p = 0"
}

funcptr是一个函数指针(void (*)(SomeClass&))。 funcptr可以指向这么多函数,并且它们有自己的调用流,因此调试起来很困难。

在调用函数指针之后,ref的派生类型从Derived变为Base,这很奇怪。为了简化我的工作,我怀疑从DerivedBase的对象切片,因此我将~Base()设为纯virtual并重新编译了整个源代码。但是没有编译器错误,这意味着没有声明Base的对象。

可能的原因是ref Derived的动态类型更改为Basedynamic_cast稍后会失败?

3 个答案:

答案 0 :(得分:2)

如果转换不明确,

dynamic_cast(指针)可以返回0。举例说明:

class O {…};
class A : public virtual O {…};
class B : public A {…};
class C : public A {…};
class D : public B, public C {…};


void f(O& p) {
  A* const a(dynamic_cast<A*>(&p));
}

void g() {
  D d;
  f(d);
}

答案 1 :(得分:2)

我不相信上面的代码实际上是因为代码示例不能编译!您无法将Base*生成的dynamic_cast<Base*>(&ref)隐式转换为Derived*

那说并且假设typeid()的输出实际上是正确的,那么对参考变化的类型ID有一些可行的解释。所有这些都表明程序中某种形式或其他形式的错误:

  1. 被叫函数会破坏对象,例如通过调用道德等同于dynamics_cast<Base*>(obj.pVoid)->~Base()
  2. 被调用函数使用展示位置obj.pVoidnew指向的地址处构造一个新对象,即:new(obj.pVoid) Base()
  3. 覆盖内存,导致在引用位置留下Base对象。
  4. 可能有更多原因......
  5. 就个人而言,我会赌第二个案例,即一个对象被构建到该位置。显然,如果没有看到被调用的函数,就无法分辨。

答案 2 :(得分:0)

dynamic_cast<>在我的具体案例中失败的原因是由于过早地delete引用了!

一旦指针或引用被delete +破坏,那么任何将其向下转换为原始类型的尝试都将导致进入“未定义行为”区域。就我而言,dynamic_cast<>失败了。

void foo (Base &ref)
{
  SomeClass obj;
  obj.pVoid = &ref;

  (*funcptr)(obj);  // ----> delete (Base*)(obj.pVoid); in one of the callbacks

  Derived *p = dynamic_cast<Derived*>(&ref);  // fails => "p = 0"
}