2

在修复庞大代码库中的错误时,我观察到一种奇怪的情况,引用的动态类型从原始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可以指向这么多的函数,并且它们有自己的调用流程,因此很难调试。

很奇怪,在调用函数指针后,派生类型refDerived变为Base。为了简化我的工作,我怀疑对象切片 from Derivedto Base,所以我将其制作~Base()为纯virtual并重新编译了整个源代码。但是没有编译器错误,这意味着没有Base被声明的对象。

ref Derived动态类型更改为Base以后dynamic_cast失败的潜在原因是什么?

4

3 回答 3

2

dynamic_cast0如果转换不明确,(指向指针)可以返回。为了显示:

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);
}
于 2012-02-11T08:06:48.267 回答
2

我不相信上面的代码实际上是因为代码示例没有编译!您不能将 aBase*结果隐式转换为dynamic_cast<Base*>(&ref)to Derived*

也就是说,假设 的输出typeid()实际上是正确的,有一些关于引用更改的类型 ID 的可行解释。所有这些都以某种形式表明程序中的错误:

  1. 被调用的函数会破坏对象,例如通过调用 的道德等价物dynamics_cast<Base*>(obj.pVoid)->~Base()
  2. obj.pVoid被调用函数在使用placement指向的地址处构造一个新对象,new例如:new(obj.pVoid) Base()
  3. 某些东西正在覆盖内存,导致将Base对象留在引用的位置。
  4. 可能还有更多的原因...

就个人而言,我会赌第二种情况,即在该位置构造一个对象。显然,没有看到被调用的函数是不可能的。

于 2012-02-11T11:10:47.973 回答
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"
}
于 2012-02-13T14:29:45.880 回答