首页 > 解决方案 > 如何在 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);

标签: c++inheritance

解决方案


您会遭受一种称为“对象切片”的效果:

SpeedDataRoad sdr;
Road currentRoad = sdr;

在第二行,按值sdr分配给,但后者的类型不适合保存完整的对象。因此,所有多余的部分都被简单地切掉,剩下的只是一个纯粹的对象,只包含原始对象的部分。currentRoadSpeedDataRoadSpeedDataRoadRoadRoadsdr

同时,由于您只剩下一个纯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_castreinterpret_cast,但你应该远离这些,除非/直到你真的,真的知道你在做什么。)

旁注 2:与 Java 的差异。

在 Java 中,我们隐式地区分原生类型和引用类型。原生的总是按值传递,引用类型总是按引用传递——好吧,Java 引用,它实际上更像是一个 C++指针(可以是null,可以重新分配),而不是 C++ 引用。在 Java 中,这是隐式发生的,在 C++ 中,您需要明确说明(另一方面,对于任何类型,您都可以同时拥有这两种行为)。

Java cast on (Java!) reference 的行为类似于 C++ dynamic_cast(在引用时,即抛出,它不会null在类型不匹配时返回)。

最后(关于我的多态性建议),在 Java 中所有函数都是隐式虚拟的,在 C++ 中,您再次必须明确(应用virtual关键字,见上文)。


推荐阅读