多态与动态铸造

Lit*_*lue 2 c++ polymorphism software-design game-development game-engine

所以我正在研究一个基于文本的RPG,我遇到了一个问题.我目前正在从角色的库存中装备武器.我试图这样做,以便我的程序可以判断他们想要装备的物品是否属于等级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;
Run Code Online (Sandbox Code Playgroud)

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

我的程序编译,一切运行正常,直到我们来到maybeWeapon->getName()程序崩溃.我已经研究了很多,但我只是不明白我做错了什么.任何答案或替代建议非常感谢!谢谢!

Chr*_*phe 8

问题

问题是你尝试动态转换为a Weapon但实际上指向的对象是构造的真实副本Item而不是子类.nullptr当你取消引用它时,这会导致a 和UB!

为什么?

假设Weapon您的库存中只有对象.你的片段中的第一条指令是你邪恶的根源:

    Item tempChosenWeapon = myInventory.chooseItem();
Run Code Online (Sandbox Code Playgroud)

这是声明Item对象的复制构造.如果源对象是a Weapon,它将被切片.

稍后你会得到一个指向这个对象的指针:

    Item *chosenWeapon = &tempChosenWeapon;
Run Code Online (Sandbox Code Playgroud)

但是这Item*并没有Weapon像你想象的那样指向一个物体.它指向一个真正的粗糙Item对象!所以当你在这里进行动态转换时:

    Weapon *maybeWeapon = dynamic_cast<Weapon*>(chosenWeapon);
Run Code Online (Sandbox Code Playgroud)

代码将发现choosenWeapon不是a Weapon*,结果dynamic_cast将是a nullptr.到目前为止,它不一定是灾难.但是当你然后对这个指针进行反驳时你会得到UB:

    maybeWeapon->getName()     // OUCH !!!!!! 
Run Code Online (Sandbox Code Playgroud)

检查是否dynamic_cast成功(即结果不是nullptr)是防止崩溃的保护,但不能解决您的根本问题.

问题甚至可能比预期的更深:myInventory.chooseItem()现实中的回报是什么类型?这是一个简单的项目吗?那么你可能已经在库存中出现了切片问题!

如果你想使用多态:

  • 你必须使用指针(最好是智能指针)或引用,以免失去对象的原始类型,就像它在这里发生的那样.
  • 如果需要复制多态对象,则不能只使用带有以下内容的赋值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; 
Run Code Online (Sandbox Code Playgroud)

如果这仍然不起作用,那么您的库存容器将存在缺陷.在这种情况下,请在使用容器代码打开单独的问题之前查看此问题