【问题标题】:dynamic_cast fails when cast a base class to derived class将基类转换为派生类时 dynamic_cast 失败
【发布时间】:2020-08-21 05:55:42
【问题描述】:

我有两个类,基类和派生类。 基类有一个虚方法。

这是我的测试示例:

class Base
{
public:
    virtual void Hello() { cout << "-> Hello Base" << endl; }
};

class Derived: public Base
{
public:
    void Hello() { cout << "-> Hello Derived" << endl; }
};

int main()
{
    Base *mBase = new Base;
    // something to do 
    ....

    Derived *mDerived = dynamic_cast<Derived*>(mBase);
    mDerived->Hello();

    return 0;
}

我希望使用mBase 转换为mDerived 之后派生的类的Hello() 方法。

但问题是,当我尝试使用dynamic_cast 时,它会导致应用程序崩溃,如果我使用reinterpret_cast 不是,则将调用Base 类的Hello() 方法。

dynamic_cast案例中的结果:

Segmentation fault (core dumped)

dynamic_cast案例中的结果:

-> Hello Base

【问题讨论】:

  • Base *mBase = new Base; 应该是Base *mBase = new Derived;
  • 您创建了一个Base 对象并尝试将其强制转换为无法工作的Derived
  • dynamic_cast&lt;Derived*&gt;(mBase); 将返回空指针,因为 mBase 不指向 Base 对象的 Base 子对象,因此您取消引用无效指针。出于同样的原因,使用 reinterpret_cast 返回的指针也将是未定义的行为 - mBase 不指向 Derived 对象的 Base 子对象。
  • 您是否希望通过强制转换来更改对象的类型?它没有。

标签: c++ polymorphism dynamic-cast reinterpret-cast


【解决方案1】:

将基类转换为派生类时dynamic_cast失败

这是应该发生的。当您将指针动态转换为指向其动态类型不是转换类型的对象时,您会得到一个空指针作为结果。

在您的示例中,您间接通过空指针并尝试调用导致未定义行为的成员函数。

使用动态转换时,您必须始终检查是否为 null。

如果我使用 reinterpret_cast...

那么行为仍然是未定义的,因为您将通过指向不存在的对象的指针进行间接访问。除非创建派生类的实例,否则不能调用其非静态成员函数。

您可以将基本实例转换为派生实例,例如:

Base b;
Derived d = b;

发生的情况是派生实例的基本子对象是从b 初始化的副本。

【讨论】:

    【解决方案2】:

    您的代码中有两个问题:Base 应该有一个虚拟析构函数,因此 Dervied 可以通过指向 Base 的指针正确地析构。然后,您没有构造 Derived 类型的对象,因此强制转换无法成功。 dynamic_cast 可能会失败,您应该检查其结果是否不是nullptr。你也忘了删除创建的对象。

    如果您确实创建了一个Derived 类型的实例,您的代码就可以工作:

    #include <iostream>
    using std::cout;
    using std::endl;
    
    class Base
    {
    public:
        virtual void Hello() { cout << "-> Hello Base" << endl; }
        virtual ~Base(){}
    };
    
    class Derived: public Base
    {
    public:
        void Hello() { cout << "-> Hello Derived" << endl; }
    };
    
    int main()
    {
        Base* mBase = new Derived;
        Derived *mDerived = dynamic_cast<Derived*>(mBase);
        if (mDerived) mDerived->Hello();
        delete mBase;
    }
    

    【讨论】:

    • 虚拟析构函数不是必须是多态类型,至少一个虚拟方法或构造函数是。
    • @Jean-BaptisteYunès 没有虚拟构造函数这样的东西。虽然从技术上讲,类可能不需要多态,但是虚拟析构函数对于delete mBase; 具有明确定义的行为是绝对必要的。
    • @Jean-BaptisteYunès 更改了措辞(并修复了漏洞)
    • @eerorika 会写析构函数,抱歉。 “虚拟析构函数是绝对必要的”当然不是。在大多数情况下是的,但是存在没有必要的情况(例如考虑没有字段的类的层次结构)。我知道这是一个建议,但它不是必需
    • @Jean-BaptisteYunès 查看标准部分 [expr.delete]
    猜你喜欢
    • 2017-01-08
    • 2013-11-17
    • 2022-10-14
    • 2021-02-18
    • 2017-07-18
    • 2011-02-04
    • 2012-09-15
    相关资源
    最近更新 更多