因此,我正在研究基于文本的RPG,但遇到了一个问题。我目前正在从角色的 list 中装备武器。我正在尝试使程序能够告诉他们他们想要装备的项目是否为Weapon类。这是相关代码的片段:

 Item tempChosenWeapon = myInventory.chooseItem();
cout << tempChosenWeapon.getName() << endl;
Item *chosenWeapon = &tempChosenWeapon;
cout << chosenWeapon->getName() << endl;//THE CODE WORKS UP TO HERE

Weapon *maybeWeapon = dynamic_cast<Weapon*>(chosenWeapon);
cout << maybeWeapon->getName() << endl;

现在,WeaponItem的子类,这就是为什么我使用动态强制转换-试图将chosenWeapon类型的Item更改为Weapon以便比较这两个类。 (我正在使用这些cout<<或测试从这些对象调用函数是否有效)。

我的程序编译完成,一切正常,直到我们进入maybeWeapon->getName()为止,在该程序中程序崩溃。我已经研究了很多,但是我只是不明白我在做什么错。任何答案或替代建议,不胜感激!谢谢!

最佳答案

问题

问题是您尝试对Weapon进行动态转换,但实际上指向的对象是由Item构造的真实副本,而不是子类。取消引用后,结果为nullptr和UB!

为什么呢

假设 list 中只有Weapon个对象。摘要中的第一条指令是您邪恶的根源:

    Item tempChosenWeapon = myInventory.chooseItem();

这是语句Item对象的副本构造。如果源对象是Weapon,则它将是 sliced

稍后,您将指针指向该对象:
    Item *chosenWeapon = &tempChosenWeapon;

但是,此Item*并不像您认为的那样指向Weapon对象。它指向一个真正的原始Item对象!因此,当您在此处进行动态投射时:
    Weapon *maybeWeapon = dynamic_cast<Weapon*>(chosenWeapon);

该代码将发现choosenWeapon不是Weapon*,并且dynamic_cast的结果将是nullptr。到现在为止,这不一定是灾难。但是,当您取消引用此指针时,您会得到UB:
    maybeWeapon->getName()     // OUCH !!!!!!



检查dynamic_cast是否成功(即结果不是nullptr)可以防止崩溃,但不能解决根本问题。

问题甚至可能比预期的更深:现实中myInventory.chooseItem()返回什么类型?它是一个普通的物品吗?然后,库存中可能已经有 slice 问题!

如果要使用多态:
  • ,您必须使用指针(最好是智能指针)或引用,以免失去对象的原始类型,就像在这里发生的那样。
  • 如果您需要复制多态对象,则不能只使用带有Item的赋值:您需要调用多态clone()函数,并确保此克隆的目标具有兼容的类型。

  • 从解决方案开始,它是这样的:
    Item* chosenWeapon = myInventory.chooseItem();  // refactor choosItem() to return a pointer.
    cout << chosenWeapon->getName() << endl;
    Weapon *maybeWeapon = dynamic_cast<Weapon*>(chosenWeapon);
    if (maybeWeapon)
        cout << maybeWeapon->getName() << endl;
    else cout << "Oops the chosen item was not a weapon" <<endl;
    

    如果仍然无法解决问题,则您的库存容器将存在缺陷。在这种情况下,请在打开带有容器代码的单独问题之前先查看this question

    关于c++ - 多态与动态类型转换,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/51974105/

    10-11 22:50