Как определить неудавшиеся попытки при использовании оператора 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
, например.