简体   繁体   English

如何在C ++中向下转换以从父实例调用子函数?

[英]How to downcast in C++ to call a child function from a parent instance?

I am trying to call a child function from a parent instance using explicit down-casting (thanks for pointing out @Aconcagua). 我试图使用显式向下转换从父实例调用子函数(感谢指出@Aconcagua)。 As a beginner in C++, I have something like this: 作为C ++的初学者,我有这样的事情:

Road currentRoad = ...;
duration = ((SpeedDataRoad) currentRoad).getSpeedProfileTime(dateinMillis, isRightDirection);

class SpeedDataRoad inherits from Road : SpeedDataRoad继承自Road

class SpeedDataRoad : public Road{ 
     double getSpeedProfileTime(long dateinMillis, bool isRightDirection) {

     ...

}

However I am getting the error: 但是我收到错误:

No matching conversion for C-style cast from 'Road' to 'SpeedDataRoad' 从'Road'到'SpeedDataRoad'的C风格演员阵容没有匹配的转换

Any suggestions on what I am doing wrong would be appreciated. 任何关于我做错的建议都将不胜感激。


Just to be clear, what I am trying to achieve in Java would be written like this and working correctly: 为了清楚起见,我在Java中尝试实现的内容将像这样编写并正常工作:

duration = ((SpeedDataRoad) currentRoad).getSpeedProfileTime(currentTime, isRightDirection);

You suffer from an effect called 'object slicing' : 您遭受称为“对象切片”的效果:

SpeedDataRoad sdr;
Road currentRoad = sdr;

At the second line, sdr is assigned by value to currentRoad , but the latter is not of appropriate type to hold a complete SpeedDataRoad object. 在第二行, sdr 按值分配给currentRoad ,但后者不适合保存完整的SpeedDataRoad对象。 So all surplus pieces of SpeedDataRoad are simply cut away and all that remains is a pure Road object only containing the Road parts of original sdr object. 因此,所有剩余的SpeedDataRoad都被简单地切除,剩下的只是一个纯粹的Road对象,只包含原始sdr对象的Road部分。

At the same time, as you only have a pure Road object left, you cannot cast it back to SpeedDataRoad object. 同时,由于您只剩下一个纯Road对象,因此无法将其SpeedDataRoadSpeedDataRoad对象。 From where should the now missing parts come from? 现在缺少的部分应该从哪里来?

This is exactly the same reason why you cannot place polymorphic types directly into a container (like std::vector ) of the base class. 这与您不能将多态类型直接放入基类的容器(如std::vector )中的原因完全相同。

What you need are either pointers (if you want to be able to re-assign) or references (to be preferred otherwise): 你需要的是指针(如果你想能够重新分配)或引用(否则是首选):

SpeedDataRoad sdr;
Road& currentRoad = sdr;
//  ^ (!)
// or:
Road* currentRoad = &sdr;

Now you can do the cast. 现在你可以做演员了。 But explicit down-casts have a smell of bad design. 但明确的下线有一种糟糕的设计气味。 You might be better off with a polymorphic approach right from the start: 从一开始就使用多态方法可能会更好:

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
    { /* ... */ }
};

Now you can simply have: 现在你可以简单地:

SpeedDataRoad sdr;
Road& currentRoad = sdr;
double profile = currentRoad.getSpeedProfileTime(0, false);

As being virtual, you'll always get the right variant of the function, no matter which sub-class we have at all and in which way it might override the function... 作为虚拟,你总是得到函数的正确变体,无论我们拥有哪个子类,以及它可能覆盖函数的方式......

Side note 1: Instead of old C-style casts, you might prefer the more modern C++ casts, you can control more fine grained what you actually want to do: 旁注1:您可能更喜欢更现代的C ++演员表,而不是旧的C风格演员阵容,您可以控制更精细的实际操作:

Road* someRoad = ...;
SpeedDataRoad* sdr = static_cast<SpeedDataRoad*>(someRoad);
SpeedDataRoad* sdr = dynamic_cast<SpeedDataRoad*>(someRoad);

You would use a static_cast , if you are 100% sure that the object only can be of desired type. 如果您100%确定对象只能是所需类型,则可以使用static_cast You avoid any run-time tests that wouldn't serve anything at all in this situation (you are 100% sure anyway, remember?). 在这种情况下,你可以避免任何根本不能提供任何服务的运行时测试(你还是100%肯定,还记得吗?)。 Curiously recurring template pattern is a typical scenario for. 奇怪的重复模板模式是一个典型的场景。

If you cannot be sure about the type, then dynamic_cast comes into play, it will do some run-time type checking and just return a null pointer (if used on pointers) or throw a std::bad_cast (if used on references), if the actual type is not of desired type (or a sub class of). 如果你不能确定类型,那么dynamic_cast就会起作用,它会做一些运行时类型检查,只返回一个空指针(如果在指针上使用)或者抛出一个std::bad_cast (如果用在引用上),如果实际类型不是所需类型(或子类)。 Such a scenario can arise when different polymorphic types are stored in a vector (as pointers to base class, see above). 当不同的多态类型存储在向量中时(作为基类的指针,见上文),可能会出现这种情况。 But again: Needing a cast at all might be a hint to your design being flawed... 但是再说一遍:需要一个演员可能暗示你的设计存在缺陷......

(For completeness: There are yet const_cast and reinterpret_cast , but you should stay away from these unless/until you really, really know what you do.) (为了完整性:还有const_castreinterpret_cast ,但你应该远离这些,除非你真的,真的知道你做了什么。)

Side note 2: Differences to Java. 附注2:与Java的差异。

In Java, we differentiate implicitly between native and reference types. 在Java中,我们在本机和引用类型之间隐式区分。 Native ones are always passed by value, reference types always by reference – well, Java reference , which actually resembles far more a C++ pointer (can be null , can be re-assigned) than a C++ reference. 原生的总是通过值传递,引用类型总是通过引用传递 - 好吧, Java引用 ,它实际上比C ++引用更像是一个C ++ 指针 (可以为null ,可以重新赋值)。 In Java, this happens implicitly , in C++, you need to be explicit about (on the other hand, you can have both behaviours for any type). 在Java中,这是隐式发生的,在C ++中,您需要明确(另一方面,您可以为任何类型都有两种行为)。

Java cast on (Java!) reference behaves like C++ dynamic_cast (on reference, ie throws, it won't return null on type mismatch). Java强制转换(Java!)引用的行为类似于C ++ dynamic_cast (在引用时,即抛出,它在类型不匹配时不会返回null )。

Finally (concerning my polymorphism recommendation), in Java all functions are implicitly virtual, in C++, you again have to be explicit about (apply the virtual keyword, see above). 最后(关于我的多态性建议),在Java中,所有函数都是隐式虚拟的,在C ++中,你必须再次明确(应用virtual关键字,见上文)。

You are slicing your SpeedDataRoad object. 您正在切割 SpeedDataRoad对象。 Unlike Java objects, which have pointer/reference semantics, C++ objects have value semantics. 与具有指针/引用语义的Java对象不同,C ++对象具有值语义。 That means, in your example, currentRoad is a Road , not a SpeedDataRoad . 这意味着,在您的示例中, currentRoadRoad ,而不是SpeedDataRoad It is a copy of the Road part of whatever SppedDataRoad is created in your ... . 它是您在...创建SppedDataRoadRoad部分的副本

To work with polymorphism is C++ you need to use references or pointers. 要使用多态性是C ++,您需要使用引用或指针。 That is, the following will not work because currentRoad is not a SpeedDataRoad : 也就是说,以下操作无效,因为currentRoad 不是 SpeedDataRoad

double foo(Road currentRoad)
{
    //...
    return ((SpeedDataRoad)currentRoad).getSpeedProfileTime(currentTime, isRightDirection);
}

int main()
{
    SpeedDataRoad road;
    foo(road);
}

While the following will work because currentRoad references a SpeedDataRoad : 虽然以下内容可行,因为currentRoad 引用SpeedDataRoad

double foo(Road& currentRoad)
//             ^---------------- Pass by reference now
{
    //...
    return dynamic_cast<SpeedDataRoad&>(currentRoad).getSpeedProfileTime(currentTime, isRightDirection);
    //     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    //     Using C++-style cast and casting reference to reference
}

int main()
{
    SpeedDataRoad road;
    foo(road);
}

In the first example, currentRoad is a copy of the Road part of road , while in the second example currentRoad is a reference to road . 在第一个示例中, currentRoadRoad road副本 ,而在第二个示例中, currentRoad是对road引用


You should also avoid C-style casts in C++. 您还应该避免在C ++中使用C风格的强制转换。 In this instance, it would be better to use dynamic_cast<SpeedDataRoad&>(currentRoad) or, if you're absolutely sure currentRoad will always be a reference to a SpeedDataRoad object, static_cast<SpeedDataRoad&>(currentRoad) . 在这种情况下,最好使用dynamic_cast<SpeedDataRoad&>(currentRoad)或者,如果您绝对确定currentRoad将始终是对SpeedDataRoad对象的引用, SpeedDataRoad static_cast<SpeedDataRoad&>(currentRoad) The former will do runtime type checking and throw an exception if currentRoad isn't a reference to a SpeedDataRoad while the latter will avoid the (small) overhead of doing runtime type checking, but will result in undefined behavior if currentRoad isn't a reference to a SpeedDataRoad . 如果currentRoad不是对SpeedDataRoad的引用,前者将执行运行时类型检查并抛出异常,而后者将避免执行运行时类型检查的(小)开销,但如果currentRoad不是引用,则会导致未定义的行为到SpeedDataRoad

That's called a downcast and not an upcast - the straightforward way to this by a dynamic_cast : 这被称为向下倾斜而不是向上 - 通过dynamic_cast直截了当地实现这一目标:

if (SpeedDataRoad* sdroad = dynamic_cast<SpeedDataRoad*>(&currentRoad); sdroad != nullptr) {
   duration = sdroad->getSpeedProfileTime(currentTime, isRightDirection);
}

If you want to check inside a function if you can downcast a pointer/refernce into a child, you need to use dynamic cast. 如果要在函数内部检查是否可以将指针/引用向下转换为子项,则需要使用动态强制转换。

void foo(Road* road){
  SpeedDataRoad* child{nullptr};
  if(child = dynamic_cast<SpeedDaraRoad*>(road){
    // Do something with SpeedDataRoad
  } else {
     // road is not an instance of SpeedDataRoad
  }
}

You can also use dynamic_cast with references, like so 你也可以使用带引用的dynamic_cast ,就像这样

cppSpeedDataRoad& child = dynamic_cast<SpeedDataRoad&>(reference_to_road);

But be carefull, as if the cast fails, std::bad_cast will be throw. 但要小心,好像演员表失败, std::bad_cast将被抛出。

In C++, we try not to use C-style casting. 在C ++中,我们尽量不使用C风格的转换。 (typename)object . (typename)object Instead, there are 4 types of type-casting. 相反,有4种类型的类型转换。

  1. static_cast<typename*>(pointer) static_cast<typename>(value) : Upcasting of pointers and type-casting for value types static_cast<typename*>(pointer) static_cast<typename>(value) :指针的上传和值类型的类型转换

  2. dynamic_cast<typename*>(pointer) : Safe-downcasting of pointers (the one you should use). dynamic_cast<typename*>(pointer)dynamic_cast<typename*>(pointer)安全向下转换(你应该使用的那个)。 It does a runtime check for upcasting, hence, there is runtime cost. 它对upcasting进行运行时检查,因此存在运行时成本。

  3. const_cast<...>(...) : Constant type const_cast<...>(...) :常量类型

  4. reinterpret_cast<typename*>(pointer) reinterpret_cast<typename>(value) : Similar to C-style cast. reinterpret_cast<typename*>(pointer) reinterpret_cast<typename>(value) :类似于C风格的强制转换。

For objects (stack allocated), I rarely use casting. 对于对象(已分配堆栈),我很少使用强制转换。 Because naturally objects in C++ has their own size, casting may change their size. 因为C ++中的自然对象有自己的大小,所以转换可能会改变它们的大小。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM