В каких ситуациях / обстоятельствах dynamic_cast <> может дать сбой? - PullRequest
2 голосов
/ 11 февраля 2012

Исправляя ошибку в огромной кодовой базе, я наблюдаю странную ситуацию, когда динамический тип ссылки изменяется с Типа оригинала 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 терпит неудачу позже?

Ответы [ 3 ]

2 голосов
/ 11 февраля 2012

Я не верю, что приведенный выше код действительно таков, потому что пример кода не компилируется!Вы не можете неявно преобразовать Base*, полученное из dynamic_cast<Base*>(&ref), в Derived*.

. Тем не менее, при условии, что выходные данные typeid() на самом деле правильные, есть несколько жизнеспособных объяснений идентификатора типа.ссылки меняются.Все они указывают на ошибку в программе в той или иной форме:

  1. Вызываемая функция уничтожает объект, например, вызывая моральный эквивалент dynamics_cast<Base*>(obj.pVoid)->~Base().
  2. ВызванныйФункция создает новый объект по адресу, указанному obj.pVoid, используя размещение new, то есть что-то вроде этого: new(obj.pVoid) Base().
  3. Что-то перезаписывает память, в результате чего объект Base остается вуказанное местоположение.
  4. Возможно, есть и другие причины ...

Лично я бы предпочел второй случай, то есть случай, то есть объект, встроенный в местоположение.Очевидно, что, не видя вызываемую функцию, невозможно сказать.

2 голосов
/ 11 февраля 2012

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);
}
0 голосов
/ 13 февраля 2012

Причина, по которой 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"
}
...