Как определить неудачные приведения с помощью оператора dynamic_cast?

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

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

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

Вы просите примеры кода, проверяющего, является ли указатель нулевым, и кода, перехватывающего исключение?   —  person nitin_cherian    schedule 16.07.2012

Нет. Я не понимаю, как броски могут потерпеть неудачу, как упоминал Скотт. Фрагмент кода определенно поможет.   —  person nitin_cherian    schedule 16.07.2012

en.wikipedia.org/wiki/Dynamic_cast   —  person nitin_cherian    schedule 16.07.2012

@JamesMcNellis: Пожалуйста, помните, что все мы начинаем как новички. В любом случае, я получил фрагмент кода от Рида.   —  person nitin_cherian    schedule 16.07.2012

См. также:  Преобразование стохастического градиентного спуска в мини-пакетный градиентный спуск
Понравилась статья? Поделиться с друзьями:
IT Шеф
Комментарии: 3
  1. nitin_cherian

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

    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
    }
    

    @LinuxPenseur Проверка предполагает, что a имеет допустимое значение. Вы должны сначала проверить это (для распределения). Проверка dynamic_cast проверяет, можно ли безопасно преобразовать A в B, поэтому проверка против b для NULL, то есть: b равно null, а a не равно null. person nitin_cherian; 16.07.2012

    @ReedCopsey: Что означает мой safe cast. В какой ситуации b может стать NULL? person nitin_cherian; 16.07.2012

    dynamic_cast используется, когда класс является полиморфным (имеет функцию virtual) и выполняет проверки во время выполнения, возвращая указатель NULL. Используя dynamic_cast, вы говорите ему делать именно то, что вы хотите, вместо преобразований C-Style, когда он пробует различные типы преобразований. dynamic_cast также используется, когда вы не знаете, какой объект нужно преобразовать, вместо static_cast, который используется для неполиморфных классов и когда известны оба типа преобразования. person nitin_cherian; 16.07.2012

    @ViniciusHorta: я не хотел видеть определение из учебника, которое совсем не то, что я просил person nitin_cherian; 16.07.2012

    @LinuxPenseur: new() не обязательно возвращает нулевой указатель в случае сбоя, вам также может потребоваться проверить исключение здесь. b станет NULL, если a не является экземпляром B. person nitin_cherian; 16.07.2012

    @g-makulik: обычная версия new никогда не вернет нулевой указатель — она либо вернет действительный указатель, либо выдаст исключение. Чтобы получить нулевой указатель при ошибке, вы используете new(nothrow) whatever; person nitin_cherian; 16.07.2012

    Прошу прощения за вопрос новичка, но эквивалентно ли if (b) if (b == NULL)? person nitin_cherian; 22.03.2016

    @Suzi это будет то же самое, что и != NULL person nitin_cherian; 22.03.2016

  2. nitin_cherian

    Основываясь на комментарии ОП («Я не понимаю, как приведения могут завершиться неудачно, как упоминал Скотт»), реальный вопрос здесь действительно что-то вроде: «Как может 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. Поскольку это так, в этом случае динамическое приведение к B * завершается успешно.

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

  3. nitin_cherian

    Вот полный пример, который показывает, как 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.

Добавить комментарий

;-) :| :x :twisted: :smile: :shock: :sad: :roll: :razz: :oops: :o :mrgreen: :lol: :idea: :grin: :evil: :cry: :cool: :arrow: :???: :?: :!: