`dynamic_cast` от Base to Derived

Да, я знаю, что downcast с использованием dynamic_cast не может скомпилироваться, если Base не является полиморфным, но моя проблема не в этом.

class Base {
    public:
        virtual void bar()
        {
            cout << "bar\n";
        }
};

class Derived: public Base {
    public:
        void foo()
        {
            cout << "foo\n";
        }
};

int main()
{
    Base *pb;
    Derived *pd;

    pb = new Derived;  //Base* points to a Derived object
    pd = dynamic_cast<Derived*>(pb); 
    pd->foo();  //outputs foo

    pb = new Base;  //Base* points to a Base object
    pd = dynamic_cast<Derived*>(pb);  
    pd->foo();  //outputs foo, too. Why?
}

Я думал, что когда pb = new Derived;, pb на самом деле указывает на объект Derived, лежит в куче. После pd = dynamic_cast<Derived*>(pb);, pd также указывает на объект Derived, поэтому pd->foo() должен быть в порядке.

Но когда pb = new Base;, то, что pb указывает на объект Base в куче, затем после pd = dynamic_cast<Derived*>(pb);, как мог pd->foo() работать? Разве dynamic_cast превратил объект Base в кучу в объект Derived?

Ответ 1

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

pd->foo();

Вы по существу вызываете Derived::foo, который является функцией в памяти, и компилятор знает, где он находится. Дело в том, что он вообще не зависит от pd. Однако, если у вас есть что-то вроде этого:

class Derived : public Base {
    private:
        int a;

    public:
        Derived() { a = 100; }

        void foo() {
            std::cout<<a<<std::endl;
        }
 };

Затем pd->foo() вызовет ошибку сегментации. Здесь ваш динамический кадр не сработал и когда вызывается Derived::foo, он передается 0 как объект this. Это было хорошо в предыдущем случае, поскольку объект this никогда не использовался. Однако во втором случае он используется и, следовательно, вызывает ошибку сегментации.

Ответ 2

В вашем foo вы не получаете доступ к this, который в этом случае должен быть NULL. Это то, что dynamic_cast возвращает, когда выполнение не может быть выполнено.

В основном вы находитесь в области "undefined".

Ответ 3

Вы используете поведение undefined. Вы должны проверить тип возврата dynamic_cast.

pd = dynamic_cast<Derived*>(pb);  

Это возвращает null, и вы вызываете функцию на указателе NULL. Все может случиться.

Ответ 4

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

pd = dynamic_cast<Derived*>(pb);  
if(pd!=NULL)
  pd->foo();

Если сбой выполнения pd имеет значение NULL. Не используйте pd, если вы не уверены, что оно имеет значение. а затем только ссылку на него