假设我有一个基类“Shape”,派生类“Triangle”,“Square”和“Circle”。 “Shape”的成员是int“shapeType”。
如果shapeType == 1,则它是一个三角形。 如果shapeType == 2,那么它就是一个正方形。 如果shapeType == 3,那么它就是一个圆圈。
我有兴趣知道,如果有一种“Shape”对象曾经是派生对象,如果有一种方法可以通过使用shapeType值“动态”向下转换为正确的派生类。< / p>
我知道我可以做一个硬代码切换语句,大致如下:
Triangle* t;
Square* s;
Circle* c;
switch (shape->shapeType) {
case 1:
t = (Triangle*)shape;
case 2:
...
}
但是,上面要求我制作一个每个派生类可能性的指针。我想知道是否有办法在不对每个类进行硬编码的情况下执行此操作,而是以某种方式确定类型类型映射,其中键是shapeType,值是类类型。
答案 0 :(得分:10)
如果他们有虚拟功能,请使用dynamic_cast
:
t = dynamic_cast<Triangle*>(shape);
if ( t )
{
//use t
}
但请注意:您应该尝试以几乎不需要使用dynamic_cast
的方式定义类和虚函数。一般来说,喜欢定义良好的接口和多态。
这是一个例子,
class Shape
{
public:
virtual ~Shape() {} //destructor must be virtual - important!
virtual double Area() const = 0;
};
class Triangle : public Shape
{
public:
Triangle(double a, double b, double c);
virtual double Area() const
{
//calculate area and return it!
}
};
Shape *s = new Triangle(10, 20, 30);
double aread = s->Area(); //calls Triangle::Area()
无需使用shapeType
变量。
答案 1 :(得分:3)
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
是用于)
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...
滥用?需要注意的是,基于指针的非投掷动态转换可以导致类似交换的代码(但是如果你不能依赖虚拟方法,那么你将不得不“切换类型”。 .. ):
if(type * p = ...)
与所有“开启类型”代码一样,这很容易出错(如果忘记处理类型会怎样?),但有时无法避免,所以值得一提。
(作为一个好奇奖金,IIRC,dynamic_cast
符号最初添加到C ++来处理这种情况并使代码更简洁......除非我遗漏了某些内容,否则这个符号在C#中未被授权)
总而言之,{{1}}依赖于RTTI(运行时类型信息),有时可以禁用它(在工作中,直到几年前,由“技术专家”决定它是不必要的,因此在我们的构建中必须禁用... Aaah,“C-with classes experts”...... )
不要让自己陷入C与C ++的战争:除非你在非常有限的环境(即嵌入式开发)中工作,否则应该激活RTTI(与异常处理等所有其他C ++特性一样)。
有关RTTI的更多信息:http://www.cplusplus.com/reference/std/typeinfo/
也许我对RTTI的Stack Overflow问题会引起你的兴趣:C++ RTTI Viable Examples
答案 2 :(得分:2)
你做错了。如果你不得不像那样贬低你很可能有一个非常严重的设计缺陷。虚拟成员函数应该是解决方案。
如果你真的必须这样做,请使用dynamic_cast
。
答案 3 :(得分:0)