安全的轉發
假設你有一個指向多型類物件的指標:
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
是不可能的。你需要在類或其父級中至少有一個虛擬函式才能使用它。