[英]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
对象,因此无法将其SpeedDataRoad
回SpeedDataRoad
对象。 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_cast
和reinterpret_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
. 这意味着,在您的示例中,
currentRoad
是Road
,而不是SpeedDataRoad
。 It is a copy of the Road
part of whatever SppedDataRoad
is created in your ...
. 它是您在
...
创建SppedDataRoad
的Road
部分的副本 。
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
. 在第一个示例中,
currentRoad
是Road
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*>(¤tRoad); 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种类型的类型转换。
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)
:指针的上传和值类型的类型转换
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进行运行时检查,因此存在运行时成本。
const_cast<...>(...)
: Constant type const_cast<...>(...)
:常量类型
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.