在修复巨大代码库中的错误时,我观察到一种奇怪的情况,即引用的动态类型从原始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
,这很奇怪。为了简化我的工作,我怀疑从Derived
到Base
的对象切片,因此我将~Base()
设为纯virtual
并重新编译了整个源代码。但是没有编译器错误,这意味着没有声明Base
的对象。
可能的原因是ref
Derived
的动态类型更改为Base
而dynamic_cast
稍后会失败?
答案 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有一些可行的解释。所有这些都表明程序中某种形式或其他形式的错误:
dynamics_cast<Base*>(obj.pVoid)->~Base()
。obj.pVoid
在new
指向的地址处构造一个新对象,即:new(obj.pVoid) Base()
。Base
对象。就个人而言,我会赌第二个案例,即一个对象被构建到该位置。显然,如果没有看到被调用的函数,就无法分辨。
答案 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"
}