In what situations / circumstances a dynamic_cast<> can fail?
-
28-04-2021 - |
سؤال
While fixing a bug in a huge code base, I am observing a strange situation, where the dynamic type of a reference changes from Original Derived
type to Base
type! I am providing the minimal code to explain the problem:
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
is a function pointer (void (*)(SomeClass&)
). funcptr
can point to so many functions and they have their own call flows, so it would be difficult to debug.
It's very strange that after the call to function pointer, the derived type of ref
changes from Derived
to Base
. To ease my work, I suspected object slicing from Derived
to Base
, so I made ~Base()
as pure virtual
and re-compiled the whole source code. But there was no compiler error, which means that there is no object of Base
being declared.
What are the potential reasons, that the dynamic type of ref
Derived
is changed to Base
and the dynamic_cast
fails later on ?
المحلول
I don't believe that the above code is like it actually is because the code example doesn't compile! You cannot implicitly convert a Base*
resulting from the dynamic_cast<Base*>(&ref)
to Derived*
.
That said and assuming that the outputs of the typeid()
are actually correct there are a few viable explanations of the type ID of the reference changing. All of these indicate an error in the program in some form or the other:
- The called function destroys the object e.g. by calling the moral equivalent of
dynamics_cast<Base*>(obj.pVoid)->~Base()
. - The called function constructs a new object at the address pointed to by
obj.pVoid
using placementnew
, i.e. something like this:new(obj.pVoid) Base()
. - Something is overwriting memory, resulting in leaving a
Base
object in the referenced location. - There are probably more reasons...
Personally, I would gamble on the second case being case, i.e. an object is constructed into the location. Obviously, without seeing the function being called it is impossible to tell.
نصائح أخرى
dynamic_cast
(to a pointer) can return 0
if the conversion is ambiguous. To illustrate:
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);
}
The reason for which the dynamic_cast<>
was failing in my specific case is due to delete
ing the reference prematurely!
Once a pointer or reference is delete
+ destructed, then any attempt to downcast it to the original type would result in entering the "undefined behavior" region. In my case the dynamic_cast<>
is failing.
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"
}