2012-03-31 121 views
0

我知道static_cast,dynamic_cast的存在。但我似乎無法找到一個具體的理由來說服自己,爲什麼要從基地派生或從基地派生,反之亦然?爲什麼我們需要從基類型轉換爲派生類型並返回?

代碼中的任何示例將不勝感激。

UPDATE

class Base 
{ 
    public: 
     void foo(); 
    private: 
     int _x; 
}; 

class Derive: Base 
{ 

}; 

Base *b = new Derive; //will b behave the same as if it's a Derive *? 
Derive *d = new Base; //I think d can't behave like a Derive * but Base *, right? 
+2

對於一個考慮周全的設計,如果不是0,則使用類型轉換的次數最少。 – 2012-03-31 12:19:53

+0

'b'將表現爲'Derive *',因爲如果您將一個虛擬成員函數添加到'Base'並覆蓋它在'Derive'中後者會在你執行'b-> f()'時被調用。 'd'的定義會給你一個編譯時錯誤。 – 2012-03-31 12:40:08

+0

@NicolaMusatti,爲什麼編譯錯誤? – Alcott 2012-03-31 12:45:18

回答

2

實際上,這些演員陣容是代碼中不確定事件的明顯標記,因此在完美的世界中,您不應該使用它們。

但在某些情況下,他們是正確的工具。

對於的static_cast,基本上有兩種情況:

1.原始轉換。

當你確實需要一些整數在一個涉及浮點的演算中被處理。

float ratio = static_cast<float>(pixel_pos.x)/static_cast<float>(pixel_pos.y); // x and y are integers because pixel positions are absolute, but we need to get a floating point value here 

2.你有一些外部API的對象,你想要得到的特定的子類。

Thing* thing = factory.create("shoe"); // Even if I don't have it's real type, I know it's a shoe! 

Shoe* shoe = static_cast<Shoe*>(thing); // I need to use Shoe interface so lets cast it. 

如果你設計了系統,也許你可以做得更好,以避免演員。但是如果你沒有,並且你使用的API提供了基本類型作爲你使用它的一種方式,那麼你沒有別的選擇,只能投射。

static_cast也很有用,因爲它可以讓你在編譯時假設一些東西,所以你應該首先使用它,因爲它需要你確定你在做什麼。

3。你不知道什麼是對象的真實類型。

但是,在運行時需要知道實際類型時有一種特殊情況,因爲在另一次無法讓您知道它。典型的情況是,當你從外部系統接收某種物體並沒有對現實類型的對象

void on_something_happen(const Event& event) // callback triggered when an event occured in the library system this callback is plugged in 
{ 
    // here I want to manage two cases 
    ThingEvent* thing_event = dynamic_cast<ThingEvent*>(&event); 

    if(thing_event) 
    { 
     // do my thing 
    } 
    else 
    { 

     // ok this event HAVE TO be a FooEvent, otherwise this should crash 
     FooEvent& foo_event = dynamic_cast<FooEvent&>(event); 
     // do my thing 

    } 




} 
+0

因此,如果'dynamic_cast'失敗,它將返回0.那麼它什麼時候會失敗呢? – Alcott 2012-03-31 12:54:55

+0

如果在投射指針時失敗,它將返回nullptr(或用C++ 03編譯器爲0),如果您投射引用,則會引發異常。您應該嘗試更多地瞭解何時使用哪個演員陣容。也知道dynamic_cast在運行時有一定的成本,這對於知道時間和內存非常重要,因爲它會添加RTTI。 – Klaim 2012-03-31 13:01:37

+0

實際上,它不返回'0'也不返回nullptr,而是返回正確指針類型的空指針(當轉換爲該指針類型時,相同的值爲0或nullptr給出*)。 – celtschk 2012-03-31 13:11:05

1

假設你有:

struct A { 
    int i; 
}; 

struct B : A { 
    char c; 
}; 

struct C : A { 
    double d; 
}; 

而且有些功能f()返回一個指針A,對此你不知道的定義。 當你這樣做:

A * a = f(); 

你怎麼知道你可以用a做什麼呢?根據上面的定義,每個BC也是A,所以你知道如果a不爲空,你可以使用它的i數據成員沒有問題。另一方面,要使用cd,您需要知道a的實際類型,並且這是通過dynamic_cast實現的。

讓我們假設你知道,實際上是一個指向B.你可以做的是:

B * b = dynamic_cast<B *>(a); 
if (b != 0) 
    b->c = 'd'; 

(是的,我知道我們假設你知道,但這樣的假設永遠永遠持有。 ..)

+0

如果'a'的實際類型不是'A *'而是'B *',那麼我可以使用'a'就好像它是一個指向'B'對象的指針? – Alcott 2012-03-31 12:29:54

+0

你需要演員。我會更新我的答案以提供示例。 – 2012-03-31 12:32:06

+0

如果'a'不是指向'B'而是'C'的指針,那麼'dynamic_cast'會失敗? – Alcott 2012-03-31 12:44:58

1

的典型情況的任何其他信息是需要的操作添加到現有的數據鍵入,但不能直接添加。

假設你有這樣的階級結構:

struct Base { 
    virtual doSomething() = 0; 
}; 

struct Derived1 : Base { 
    virtual doSomething(); 
    int x,y; 
}; 

struct Derived2 : Base { 
    virtual doSomething(); 
    float a,b; 
}; 

現在你寫傳遞一個基地&功能:

void f(Base& base); 

您希望能夠打印有關的基本信息,但無論出於何種原因,您都不允許修改Base來添加此操作(例如,它是商業圖書館的一部分)。在這種情況下,您可能需要這樣做:

void f(Base& base) 
{ 
    if (Derived1* p=dynamic_cast<Derived1*>(&base)) { 
    cout << "Derived1{" << p->x << "," << p->y << "}\n"; 
    } 
    else if (Derived2* p=dynamic_cast<Derived2*>(&base)) { 
    cout << "Derived2{" << p->a << "," << p->b << "}\n"; 
    } 
    else { 
    cout << "Unknown type\n"; 
    } 
} 

儘管這在面向對象的語言中通常被認爲是不好的風格。一個問題是,如果您將新類添加到層次結構中,那麼編譯器無法幫助您找到需要添加代碼以處理該新類實例操作的位置。

+0

我會去與一個門面/適配器/無論這被稱爲,而不是一個字符串if(dynamic_cast)else if(dynamic_cast)...。有些情況下可以進行動態演員表演,但**從不**可以演繹線性演員陣容。 – 2012-03-31 12:56:44

+0

因此,如果傳入的Base&base實際上是對Derived1或Derived2對象的引用,那麼'dynamic_cast'將成功,但如果'base'是對'Base'對象的引用,那麼'dynamic_cast'將返回0? – Alcott 2012-03-31 12:58:24

+0

@Alcott:由於'Base'是抽象的,因此不能有任何'Base'對象不是從'Base'派生的動態類型。 – celtschk 2012-03-31 13:06:15

相關問題