基于变量的C ++向下转换为派生类

时间:2011-06-24 17:40:35

标签: c++ inheritance casting

假设我有一个基类“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,值是类类型。

4 个答案:

答案 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#中未被授权)

RTTI

总而言之,{{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)