安全的转发
假设你有一个指向多态类对象的指针:
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
转变为其派生的和更具体的形状之一,如 Square
或 Circle
。
为什么要垂头丧气?
大多数情况下,你不需要知道哪个是对象的真实类型,因为虚函数允许你独立于其类型操作对象:
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
是不可能的。你需要在类或其父级中至少有一个虚函数才能使用它。