如何在C++从父实例调用子函数
How to downcast in C++ to call a child function from a parent instance?
我正在尝试使用显式向下转换从父实例调用子函数(感谢您指出@Aconcagua)。作为C++的初学者,我有这样的东西:
Road currentRoad = ...;
duration = ((SpeedDataRoad) currentRoad).getSpeedProfileTime(dateinMillis, isRightDirection);
类SpeedDataRoad
继承自Road
:
class SpeedDataRoad : public Road{
double getSpeedProfileTime(long dateinMillis, bool isRightDirection) {
...
}
但是我收到错误:
C 样式转换从"道路"到"速度数据道路"的转换不匹配
任何关于我做错了什么的建议将不胜感激。
需要明确的是,我试图在 Java 中实现的目标将像这样编写并正常工作:
duration = ((SpeedDataRoad) currentRoad).getSpeedProfileTime(currentTime, isRightDirection);
您遭受一种称为"对象切片">的效果:
SpeedDataRoad sdr;
Road currentRoad = sdr;
在第二行,sdr
按值分配给currentRoad
,但后者的类型不适合保存完整的SpeedDataRoad
对象。因此,所有多余的SpeedDataRoad
都被简单地切掉,剩下的只是一个纯粹的Road
对象,只包含原始sdr
对象的Road
部分。
同时,由于您只剩下一个纯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 样式转换,而不是旧的 C 样式转换,您可以控制更细粒度的您实际想要做的事情:
Road* someRoad = ...;
SpeedDataRoad* sdr = static_cast<SpeedDataRoad*>(someRoad);
SpeedDataRoad* sdr = dynamic_cast<SpeedDataRoad*>(someRoad);
如果您 100% 确定对象只能是所需类型,则可以使用static_cast
。在这种情况下,您可以避免任何根本无法提供任何服务的运行时测试(无论如何,您都是 100% 确定的,还记得吗?奇怪的重复出现的模板模式是典型的方案。
如果您无法确定类型,那么dynamic_cast
发挥作用,它将执行一些运行时类型检查,如果实际类型不是所需的类型(或子类),则只返回一个空指针(如果用于指针)或抛出一个std::bad_cast
(如果用于引用)。当不同的多态类型存储在向量中时,可能会出现这种情况(作为基类的指针,见上文)。但再说一遍:需要演员阵容可能暗示你的设计存在缺陷......
(为了完整起见:还有const_cast
和reinterpret_cast
,但你应该远离这些,除非/直到你真的知道你在做什么。
旁注2:与Java的差异。
在 Java 中,我们隐式区分本机类型和引用类型。本机的总是按值传递,引用类型总是通过引用传递——嗯,Java引用,它实际上更像一个C++指针(可以null
,可以重新分配),而不是C++引用。在 Java 中,这是隐式发生的,在 C++ 中,您需要明确(另一方面,对于任何类型的行为,您可以同时具有这两种行为)。
Javacast on (Java!) 引用的行为类似于 C++dynamic_cast
(在引用时,即抛出,它不会在类型不匹配时返回null
)。
最后(关于我的多态性建议),在Java中,所有函数都是隐式虚拟的,在C++中,你必须再次明确(应用virtual
关键字,见上文)。
您正在切割SpeedDataRoad
对象。 与具有指针/引用语义的 Java 对象不同,C++对象具有值语义。 这意味着,在您的示例中,currentRoad
是Road
,而不是SpeedDataRoad
。 它是在您的...
中创建的任何SppedDataRoad
的Road
部分的副本。
要使用多态性C++需要使用引用或指针。 也就是说,以下内容将不起作用,因为currentRoad
不是SpeedDataRoad
:
double foo(Road currentRoad)
{
//...
return ((SpeedDataRoad)currentRoad).getSpeedProfileTime(currentTime, isRightDirection);
}
int main()
{
SpeedDataRoad road;
foo(road);
}
虽然以下内容将起作用,因为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);
}
在第一个示例中,currentRoad
是road
Road
部分的副本,而在第二个示例中,currentRoad
是对road
的引用。
您还应该避免在C++中使用 C 型转换。 在这种情况下,最好使用dynamic_cast<SpeedDataRoad&>(currentRoad)
,或者,如果您绝对确定currentRoad
将始终是对SpeedDataRoad
对象的引用,则static_cast<SpeedDataRoad&>(currentRoad)
。 前者将执行运行时类型检查,如果currentRoad
不是对SpeedDataRoad
的引用,则引发异常,而后者将避免执行运行时类型检查的(小)开销,但如果currentRoad
不是对SpeedDataRoad
的引用,则会导致未定义的行为。
这被称为向下投射而不是向上投射 -dynamic_cast
的直接方法:
if (SpeedDataRoad* sdroad = dynamic_cast<SpeedDataRoad*>(¤tRoad); sdroad != nullptr) {
duration = sdroad->getSpeedProfileTime(currentTime, isRightDirection);
}
如果要在函数内部检查是否可以将指针/引用向下转换为子项,则需要使用动态强制转换。
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
}
}
您还可以将dynamic_cast
与引用一起使用,例如
cppSpeedDataRoad& child = dynamic_cast<SpeedDataRoad&>(reference_to_road);
但要小心,好像如果演员阵容失败,std::bad_cast
就会被扔掉。
在C++中,我们尽量不使用 C 样式转换。(typename)object
. 相反,有 4 种类型的类型转换。
-
static_cast<typename*>(pointer) static_cast<typename>(value)
:指针的向上转换和值类型的类型转换 -
dynamic_cast<typename*>(pointer)
:指针的安全向下转换(您应该使用的指针)。它对上行转换进行运行时检查,因此存在运行时成本。 -
const_cast<...>(...)
: 常量型 -
reinterpret_cast<typename*>(pointer) reinterpret_cast<typename>(value)
: 类似于C型演员表。
对于对象(堆栈分配),我很少使用强制转换。由于C++中的自然物体有自己的尺寸,因此铸造可能会改变其尺寸。
- OpenGL - 在抛出"__gnu_cxx::recursive_init_error"实例后终止调用?
- 从基类实例调用派生类方法而不进行强制转换
- 如何在C++从父实例调用子函数
- 为 std::shared_ptr 实例调用 Operator()
- 跨类的多个实例调用函数一次
- 如何从派生类实例调用父类重载函数
- 如何在C++中跨实例调用方法
- 防止同一类的实例调用公共功能(C )
- 使用Objective C类实例调用C函数
- 从结构实例调用变量语法问题C++
- 从基类实例调用派生类方法
- C++从基类实例调用派生函数
- 是否可以从不存在的实例调用方法?
- 如何使用派生类实例调用基类重载的func,
- 为singleton实例调用函数创建一个别名
- 用结构体的所有实例调用函数
- 从继承的类实例调用静态成员
- 当我从类实例调用方法时,我的C程序崩溃了,下面是代码
- 当尝试使用类的实例调用方法时,调试断言错误
- C++:从派生实例调用 base 中的纯虚拟方法重载