我正在尝试使用显式向下转换从父实例调用子函数(感谢指出@Aconcagua)。作为 C++ 的初学者,我有这样的事情:
Road currentRoad = ...;
duration = ((SpeedDataRoad) currentRoad).getSpeedProfileTime(dateinMillis, isRightDirection);
类 SpeedDataRoad
继承自 Road
:
class SpeedDataRoad : public Road{
double getSpeedProfileTime(long dateinMillis, bool isRightDirection) {
...
}
但是我得到了错误:
No matching conversion for C-style cast from 'Road' to 'SpeedDataRoad'
如有任何关于我做错的建议,我们将不胜感激。
需要说明的是,我想用 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% 确定对象仅 可以是所需的类型。您可以避免在这种情况下根本无法提供任何服务的任何运行时测试(无论如何您都 100% 确定,还记得吗?)。 Curiously recurring template pattern是一个典型的场景。
如果你不能确定类型,那么 dynamic_cast
开始发挥作用,它会做一些运行时类型检查并只返回一个空指针(如果用在指针上)或抛出一个 std::bad_cast
(如果用于引用),如果实际类型不是所需类型(或其子类)。当不同的多态类型存储在一个 vector 中(作为指向基类的指针,见上文)时,就会出现这种情况。但同样:根本需要类型转换可能暗示您的设计存在缺陷......
(为了完整性:还有 const_cast
和 reinterpret_cast
,但你应该远离这些,除非/直到你真的,真的知道你做什么。)
旁注 2:与 Java 的差异。
在 Java 中,我们隐式区分 native 类型和引用类型。 native 类型始终按值传递,引用类型始终按引用传递 - 好吧,Java 引用,它实际上更类似于 C++ 指针(可以是null
,可以重新分配)而不是 C++ 引用。在 Java 中,这隐式发生,在 C++ 中,您需要明确(另一方面,任何类型都可以有这两种行为)。
Java 在 (Java!) 引用上的转换行为类似于 C++ dynamic_cast
(在引用上,即抛出,它不会在类型不匹配时返回 null
)。
最后(关于我的多态性建议),在 Java 中所有函数都是隐式虚拟的,在 C++ 中,您必须再次明确(应用 virtual
关键字,请参阅以上)。
关于c++ - 如何在 C++ 中向下转换以从父实例调用子函数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57657546/