安全的转发

假设你有一个指向多态类对象的指针:

Shape *ps;                       // see example on defining a polymorphic class
ps =  get_a_new_random_shape();  // if you don't have such a function yet, you 
                                 // could just write ps = new Square(0.0,0.0, 5);

垂头丧气将从一般的多态 Shape 转变为其派生的和更具体的形状之一,如 SquareCircle

为什么要垂头丧气?

大多数情况下,你不需要知道哪个是对象的真实类型,因为虚函数允许你独立于其类型操作对象:

std::cout << "Surface: " << ps->get_surface() << std::endl; 

如果你不需要任何垂头丧气,你的设计将是完美的。

但是,有时你可能需要向下倾斜。一个典型的例子是当你想要调用仅为子类存在的非虚函数时。

考虑例如圈子。只有圆圈有直径。所以这个类定义为:

class Circle: public Shape { // for Shape, see example on defining a polymorphic class
    Point center;
    double radius;
public: 
    Circle (const Point& center, double radius)
       : center(center), radius(radius) {}

    double get_surface() const override { return r * r * M_PI; }   

    // this is only for circles. Makes no sense for other shapes 
    double get_diameter() const { return 2 * r; }
};

get_diameter() 成员函数仅适用于圆圈。它没有为 Shape 对象定义:

Shape* ps = get_any_shape();
ps->get_diameter(); // OUCH !!! Compilation error 

如何垂头丧气?

如果你确定知道 ps 指向一个圆圈,你可以选择 static_cast

std::cout << "Diameter: " << static_cast<Circle*>(ps)->get_diameter() << std::endl;

这样就可以了。但这是非常危险的:如果 ps 出现在除了 Circle 以外的任何其他内容之外,代码的行为将是不确定的。

所以,不应该玩俄罗斯轮盘赌,你应该安全地使用 dynamic_cast。这是针对多态类的:

int main() {
    Circle circle(Point(0.0, 0.0), 10);
    Shape &shape = circle;

    std::cout << "The shape has a surface of " << shape.get_surface() << std::endl;

    //shape.get_diameter();   // OUCH !!! Compilation error 

    Circle *pc = dynamic_cast<Circle*>(&shape); // will be nullptr if ps wasn't a circle 
    if (pc) 
        std::cout << "The shape is a circle of diameter " << pc->get_diameter() << std::endl;
    else
        std::cout << "The shape isn't a circle !" << std::endl; 
}        

请注意,对于非多态的类,dynamic_cast 是不可能的。你需要在类或其父级中至少有一个虚函数才能使用它。