Почему static_cast работает, но dynamic_cast терпит неудачу?

#c #c 11 #casting

#c #c 11 #Кастинг

Вопрос:

Предположим, у меня есть базовый класс и 2 классифицированных производных от него. Все производные классы имеют свою собственную логику, несовместимую между ними. Тестовое приложение выглядит следующим образом:

 #include <iostream>
#include <vector>
    
class Base
{
public:
    Base(int x) { m_x = x; }
    virtual int getResult() { return m_x; }
protected:
    int m_x;
};


class DerivedA : public Base
{
public:
    DerivedA(int x) : Base(x) {}
    int getResult() override { return m_x * 2; }
};

class DerivedB : public Base
{
public:
    DerivedB(int x) : Base(x) {}
    int getResult() override { return m_x * m_x; }
};


int main()
{
    std::vector<Base *> objects;
    objects.push_back(new DerivedA(1));
    objects.push_back(new DerivedB(2));
    objects.push_back(new DerivedA(3));
    objects.push_back(new DerivedB(4));

    for(Base *object: objects)
    {
        DerivedA *obj = static_cast<DerivedA *>(object);
        if(obj != nullptr)
        {
            std::cout << obj->getResult() << std::endl;
        }
    }

    return 0;
}
 

Я ожидаю получить только 2 результата, т.Е. Что Только экземпляры типа DerivedA могут быть приведены к DerivedA , но не DerivedB . Но, к моему удивлению, это не так. static_cast выполняет приведение DerivedB к DerivedA без проблем. Почему это происходит? Я могу понять это, если бы я привел к Base , но не к этому. В то же время dynamic_cast работает так, как ожидалось, т.Е. Приведение DerivedB к DerivedA сбою.

Комментарии:

1. static_cast предполагается, что программист поступает правильно. Когда вы этого не сделаете, разыменование результирующего указателя будет иметь неопределенное поведение.

2. В этом как раз и заключается разница между двумя типами приведений. dynamic_cast проверяет преобразование во время выполнения и завершается ошибкой, если типы не совпадают. static_cast проверяет типы во время компиляции и завершает работу успешно (т. Е. компилирует), если не может определить, что они не могут совпадать.

3. Радость неопределенного поведения заключается в том, что вещи в основном могут работать, что затрудняет поиск возможного сбоя

4. Я всегда предполагал, что reinterpret_cast это зависит от программиста, но static_cast выполняет проверку во время компиляции.

5. @folibis: Он действительно выполняет приведение во время компиляции, но поведение по-прежнему не определено.

Ответ №1:

Поведение при разыменовании указателя, являющегося результатом преобразования a static_cast из a DerivedB* в a DerivedA* , не определено. Это строгое нарушение псевдонимов.

Комментарии:

1. Приведение к несвязанному типу — это просто UB. Строгое наложение псевдонимов имеет важное значение, когда приведение работало бы в противном случае.