Как определить неудавшиеся попытки при использовании оператора dynamic_cast?

Scott Meyer в своей книге Effective C++ говорит, что dynamic_cast используется для безопасного удаления или иерархии наследования. То есть вы используете dynamic_cast для указания указателей или ссылок на объекты базового класса в указатели или ссылки на объекты базового класса с производными или родственными классами таким образом, чтобы вы могли определить, удалось ли кастам выполнить.

Неудачные отбрасывания указываются нулевым указателем (при указателях каста) или исключением (при ссылках на литье).

Я хотел бы получить два фрагмента кода, показывающие неудавшийся листинг в случае указания указателя на литье и ссылки на литье.

Ответ 1

Для указателей это простая проверка нуля:

A* a = new A();
B* b = dynamic_cast<B*>(a);

if (b == NULL)
{
    // Cast failed
}

Для ссылок вы можете поймать:

try {
    SomeType &item = dynamic_cast<SomeType&>(obj);
}
catch(const std::bad_cast& e) {
    // Cast failed
}

Ответ 2

Основываясь на комментарии OP ( "Я не понимаю, как отбрасывание может потерпеть неудачу, как упоминалось Скоттом" ), реальный вопрос здесь действительно выглядит примерно так: "Как может произойти сбой dynamic_cast?"

Время, в течение которого произойдет сбой, - это когда тип цели не соответствует динамическому типу объекта. Для простого примера:

struct A {
   virtual ~A() {}
};

class B : public A {};

int main() { 
    A *a = new A;

    B *b = dynamic_cast<B *>(a);    // should fail
    if (b == NULL)
        std::cout << "First cast failed.\n";

    A *c = new B;
    b = dynamic_cast<B *>(c);       // should succeed
    if (b == NULL)
        std::cout << "Second cast failed.\n";
    return 0;
}

Здесь, хотя a может указывать на объект типа B, он фактически указывает на объект типа a. Когда мы пытаемся сделать dynamic_cast, чтобы он указывал на B, это терпит неудачу. Во второй попытке мы снова имеем указатель, который не только мог, но и указывать на объект типа B. Так как это так, в этом случае успех dynamic_cast до B * преуспевает.

Основная ситуация не меняется (значительно) для ссылочного примера, просто a, B и c становятся ссылками вместо указателей, и мы отмечаем провал, вылавливая исключение (которое @ReedCopsey имеет уже продемонстрировал достаточно хорошо, что я не думаю, что у меня есть что-то новое для добавления).

Ответ 3

Вот полный пример, показывающий, как dynamic_cast может не создать указатель.

class A
{
public:
    virtual void Foo();
};

class B: public A
{
};

class C: public A
{
};

void test()
{
    A a;
    B b;
    A* pA = &b;
    B* pB = dynamic_cast<B*>(pA);  // this works OK, returns the expected pointer
    C* pC = dynamic_cast<C*>(pA);  // this returns NULL because B isn't a C
}

В реальном мире вы будете пытаться наложить указатели, которые были не так просто созданы, возможно, они взяты из vector, например.