Question

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 ?

Was it helpful?

Solution

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:

  1. The called function destroys the object e.g. by calling the moral equivalent of dynamics_cast<Base*>(obj.pVoid)->~Base().
  2. The called function constructs a new object at the address pointed to by obj.pVoid using placement new, i.e. something like this: new(obj.pVoid) Base().
  3. Something is overwriting memory, resulting in leaving a Base object in the referenced location.
  4. 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.

OTHER TIPS

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 deleteing 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"
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top