c++ - 多态性和动态铸造
问题描述
所以我正在开发一个基于文本的 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;
现在,Weapon
是 的子类Item
,这就是我使用动态强制转换的原因 - 试图将 type 更改chosenWeapon
为Item
typeWeapon
以便比较两个类。(我正在使用这些cout<<
s 或测试从这些对象调用函数是否有效)。
我的程序编译了,一切都运行良好,直到maybeWeapon->getName()
程序崩溃。我已经研究了很多,但我只是不明白我做错了什么。非常感谢任何答案或替代建议!谢谢!
解决方案
问题
问题是您尝试对 a 进行动态强制转换,Weapon
但实际上指向的对象是构造的真实副本Item
而不是子类。nullptr
当您取消引用它时,这会导致 a和 UB !
为什么 ?
假设Weapon
您的库存中只有对象。您的代码段中的第一条指令是您邪恶的根源:
Item tempChosenWeapon = myInventory.chooseItem();
这是语句是Item
对象的复制构造。如果源对象是 a Weapon
,它将被切片。
稍后您将指针指向该对象:
Item *chosenWeapon = &tempChosenWeapon;
但这并不像你想象Item*
的那样指向一个对象。Weapon
它指向一个真正的粗糙Item
物体!因此,当您在这里进行动态转换时:
Weapon *maybeWeapon = dynamic_cast<Weapon*>(chosenWeapon);
代码会发现choosenWeapon
不是 a Weapon*
,结果dynamic_cast
是 a nullptr
。到目前为止,这不一定是一场灾难。但是当你取消引用这个指针时,你会得到 UB:
maybeWeapon->getName() // OUCH !!!!!!
解决方案
检查是否dynamic_cast
成功(即结果不是nullptr
)可以防止崩溃,但不会解决您的根本问题。
甚至有可能问题比预期的更深:myInventory.chooseItem()
实际返回的是什么类型?它是一个普通的 Item 吗?那么您可能已经在库存中遇到了切片问题!
如果你想使用多态性:
- 您必须使用指针(最好是智能指针)或引用,以免丢失对象的原始类型,就像这里发生的那样。
- 如果您需要复制多态对象,则不能只使用带有
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;
如果这仍然不起作用,那么您的库存容器将有缺陷。在这种情况下,请先查看这个问题,然后再用您的容器代码打开一个单独的问题
推荐阅读
- javascript - 如何使用递归、休息/扩展运算符和解构将数组中的这些数字加倍?
- c++ - 如何在 C++ 代码块中从 oracle 数据库中读取表?我
- opencv - 如何配置 while 循环以使用 picamera?
- angular - Angular 单元测试为 Http Post 返回“无法读取未定义的属性”
- mysql - 如何在 django2 中编写 raw like 查询?
- javascript - Javascript变量传递带有撇号的文本字符串,被切断了吗?
- c++ - C++ 隐式类到 char* 的转换
- python - 组合不同的列
- java - 使用 Kotlin DSL 配置 Java 规范的 Gradle 编译错误
- c# - C# 编写目录