安全的轉發

假設你有一個指向多型類物件的指標:

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 是不可能的。你需要在類或其父級中至少有一個虛擬函式才能使用它。