dynamic_cast 是您问题的答案。
说明
它用于从基类向下转换为派生类,同时确保在派生类不是您所想的情况下转换失败。例如:
void foo(Shape * p_shape)
{
Triangle * t = dynamic_cast<Triangle *>(p_shape) ;
// if p_shape is a triangle, or derives from triangle,
// then t is non-NULL, and you can use it
}
关键是即使 p_shape 不完全是三角形,t 也将是非 NULL,但仍然继承三角形。例如,在这种情况下:
Shape
|
+-- Square
|
+-- Triangle
|
+-- EquilateralTriangle
|
+-- RectangleTriangle
如果 shape 是 Triangle、EquilateralTriangle 或 RectangleTriangle,则 t 不会为 NULL,这比使用常数标记确切类型的初始解决方案强大得多。
请注意,要让dynamic_cast 在一个类上工作,这个类至少应该有一个虚方法(这通常是在使用dynamic_cast 的树继承层次结构中的情况 i>)
投掷dynamic_cast
您可以使用引用而不是使用指针,但是使用引用,因为dynamic_cast 无法返回“失败的引用”,它会抛出一个std::bad_cast,如果需要,您可以捕获:
void foo(Shape & p_shape)
{
Triangle & t = dynamic_cast<Triangle &>(p_shape) ;
// if p_shape is a triangle, or derives from triangle,
// then the dynamic_cast succeeds.
// If not, a std::bad_cast is thrown
}
dynamic_cast滥用?
需要注意的是,基于指针的非抛出动态转换可能会导致类似开关的代码(但如果您不能依赖虚拟方法,那么您将不得不“切换类型”。 ..):
void foo(Shape * p_shape)
{
if(Triangle * t = dynamic_cast<Triangle *>(p_shape))
{
// if p_shape is a triangle, then t is non-NULL,
// and you can use it
}
else if(Square * s = dynamic_cast<Square *>(p_shape))
{
// if p_shape is a square, then t is non-NULL
// and you can use it
}
// etc...
与所有“打开类型”代码一样,这很容易出错(如果您忘记处理类型怎么办?),但有时无法避免,因此值得一提。
(作为好奇的奖励,IIRC,if(type * p = ...) 符号最初被添加到 C++ 中以处理这种情况并减少代码冗长......除非我错过了什么,这种符号在 C# 中是不被授权的)
RTTI
总而言之,dynamic_cast 依赖于 RTTI(运行时类型信息),它有时可以被禁用(在工作中,直到几年前,“技术专家”才认为它是不必要的因此必须在我们的构建中禁用...啊啊,“C 类专家”...)
不要让自己陷入 C 与 C++ 的战争:除非您在非常受限的环境中工作(即嵌入式开发),否则应该激活 RTTI(因为所有其他 C++ 功能,如异常处理)。
更多关于 RTTI 的信息:http://www.cplusplus.com/reference/std/typeinfo/
也许我关于 RTTI 的 Stack Overflow 问题会让你感兴趣:C++ RTTI Viable Examples