c++ - 如何在 C++ 中向下转换以从父实例调用子函数?
问题描述
我正在尝试使用显式向下转换从父实例调用子函数(感谢您指出@Aconcagua)。作为 C++ 的初学者,我有这样的事情:
Road currentRoad = ...;
duration = ((SpeedDataRoad) currentRoad).getSpeedProfileTime(dateinMillis, isRightDirection);
类SpeedDataRoad
继承自Road
:
class SpeedDataRoad : public Road{
double getSpeedProfileTime(long dateinMillis, bool isRightDirection) {
...
}
但是我收到错误:
从“Road”到“SpeedDataRoad”的 C 风格转换没有匹配的转换
任何关于我做错了什么的建议将不胜感激。
为了清楚起见,我试图在 Java 中实现的目标将是这样编写的并且可以正常工作:
duration = ((SpeedDataRoad) currentRoad).getSpeedProfileTime(currentTime, isRightDirection);
解决方案
您会遭受一种称为“对象切片”的效果:
SpeedDataRoad sdr;
Road currentRoad = sdr;
在第二行,按值sdr
分配给,但后者的类型不适合保存完整的对象。因此,所有多余的部分都被简单地切掉,剩下的只是一个纯粹的对象,只包含原始对象的部分。currentRoad
SpeedDataRoad
SpeedDataRoad
Road
Road
sdr
同时,由于您只剩下一个纯Road
对象,因此您不能将其转换回SpeedDataRoad
对象。现在缺失的部分应该从哪里来?
这与为什么不能将多态类型直接放入std::vector
基类的容器(如 )中的原因完全相同。
您需要的是指针(如果您希望能够重新分配)或引用(否则首选):
SpeedDataRoad sdr;
Road& currentRoad = sdr;
// ^ (!)
// or:
Road* currentRoad = &sdr;
现在你可以做演员了。但是明确的向下转型有一种糟糕设计的味道。从一开始就使用多态方法可能会更好:
class Road
{
public:
virtual double getSpeedProfileTime(long, bool) = 0;
// ^ pure virtual
// alternatively, you can provide a default implementation
};
class SpeedDataRoad : public Road
{
public:
double getSpeedProfileTime(long, bool) override
{ /* ... */ }
};
现在你可以简单地拥有:
SpeedDataRoad sdr;
Road& currentRoad = sdr;
double profile = currentRoad.getSpeedProfileTime(0, false);
由于是虚拟的,无论我们拥有哪个子类以及它可能以何种方式覆盖该函数,您将始终获得该函数的正确变体......
旁注 1:您可能更喜欢更现代的 C++ 类型转换,而不是旧的 C 样式转换,您可以更精细地控制您实际想要做的事情:
Road* someRoad = ...;
SpeedDataRoad* sdr = static_cast<SpeedDataRoad*>(someRoad);
SpeedDataRoad* sdr = dynamic_cast<SpeedDataRoad*>(someRoad);
static_cast
如果您 100% 确定对象只能是所需类型,则可以使用 a 。在这种情况下,您可以避免任何根本无法提供任何服务的运行时测试(无论如何,您是 100% 确定的,还记得吗?)。奇怪地重复出现的模板模式是一个典型的场景。
如果你不能确定类型,那么dynamic_cast
它就会发挥作用,它会进行一些运行时类型检查std::bad_cast
,如果实际类型是,则只返回一个空指针(如果用于指针)或抛出一个(如果用于引用)不是所需的类型(或子类)。当不同的多态类型存储在向量中时(作为指向基类的指针,请参见上文),可能会出现这种情况。但同样:根本需要演员表可能暗示您的设计存在缺陷......
(为了完整性:还有const_cast
和reinterpret_cast
,但你应该远离这些,除非/直到你真的,真的知道你在做什么。)
旁注 2:与 Java 的差异。
在 Java 中,我们隐式地区分原生类型和引用类型。原生的总是按值传递,引用类型总是按引用传递——好吧,Java 引用,它实际上更像是一个 C++指针(可以是null
,可以重新分配),而不是 C++ 引用。在 Java 中,这是隐式发生的,在 C++ 中,您需要明确说明(另一方面,对于任何类型,您都可以同时拥有这两种行为)。
Java cast on (Java!) reference 的行为类似于 C++ dynamic_cast
(在引用时,即抛出,它不会null
在类型不匹配时返回)。
最后(关于我的多态性建议),在 Java 中所有函数都是隐式虚拟的,在 C++ 中,您再次必须明确(应用virtual
关键字,见上文)。
推荐阅读
- java - 从 JNI 更改 C++ 中定义的数组的问题
- microsoft-graph-api - 尝试发起 PSTN 呼叫返回“呼叫源身份无效”
- c# - 为什么垃圾收集器不触发终结器?
- python - 正则表达式使用换行符搜索两个关键字之间的所有子字符串
- flutter - Flutter 过滤器 Firebase 数据库
- android - 在 Project View Andoid Studio 中获取此空文件夹和错误
- nuget - 无法在 Arch linux 中加载源 https://api.nuget.org/v3/index.json 的服务索引
- python - 如何使用 Odoo 14 中的继承向现有的 Many2One 字段添加元素?
- android - RecyclerView ItemDecoration vs Margin 用于 RecyclerView 项目之间的间距
- c++ - Qt Creator 和海康威视 SDK