2011-09-13 35 views
3

這裏是我的測試例子:如何使用reinterpret_cast的強制轉換爲派生類指針在C++中

struct base { 
    virtual ~base(){} 
    int x; 
}; 

struct derived: public virtual base { 
    base * clone() { 
     return new derived; 
    } 
    derived(): s("a") {} 
    std::string s; 
}; 

int main() { 
    derived d; 
    base * b = d.clone(); 
    derived * t = reinterpret_cast<derived*>(b); 
    std::cout << t->s << std::endl; 
    return 0; 
} 

它崩潰,在那裏我打印■行。由於「b」是派生類的指針,因此reinterpret_cast應該可以工作。我想知道它爲什麼崩潰。同時,如果我用dynamic_cast替換reinterpret_cast,那麼它可以工作。

回答

8

即使b在這裏動態derived類型的,你必須使用dynamic_cast。這是dynamic_cast用於在運行時動態地將基類的指針轉換爲派生類。

reinterpret_cast將原始指針視爲原始指針並將其視爲派生類型。但是,由於繼承的原因,必須對指針指向正確的方法調度表進行微調,這正是dynamic_cast所要做的。

+0

如果不是虛擬繼承,'static_cast'會起作用。當你確定你的基地是派生的時候,你不需要去運行。 –

+0

是的,你是對的,如果我刪除_virtual_並使它只是結構派生:public base,那麼reinterpret_cast也可以(至少沒有崩潰) – Andrei

+0

K-ballo:你知道,構建庫時的醜陋的繼承規則(你不知道未來是否會通過兩條路徑繼承一個類)爲了防萬一,有時會強迫你濫用虛擬繼承。 –

1

不要reinterpret_cast它,它會引起多重或虛擬繼承問題,就像你的情況一樣。簡單地通過static_cast在這裏完成這項工作?

要知道爲什麼,搜索虛擬繼承的實現。常見的一種是在對象中存儲一個指向基類的指針,所以虛擬基地不會共享與其派生類相同的地址。使用多重繼承時也有類似的情況。

總之,reinterpret_cast不能做的比鑄造指向整數和背面(如果有足夠的規模在INT包含指針)等等。

+0

在這種情況下,編譯器說, 錯誤:無法從基地「基地」通過虛擬基「基地」 – Andrei

+1

不能靜態地從虛擬基礎施法「衍生」派生類型轉換。所以你必須在這裏使用'dynamic_cast'(這意味着運行時懲罰)。除此之外,我同意'reinterpret_cast'是**惡**! – bitmask

+0

然後你應該用'dynamic_cast'來代替。 –

0

至於其他的答案在這裏建議,你不能以這種方式,因爲指針的base實際上從值指針derived不同使用reinterpret_cast。有效指針在運行時推導出來,這就是爲什麼你必須使用dynamic_caststatic_cast不能在下工作,因爲您不知道在設計時通過哪個中間類型派生的類(您要轉換的類)是從您指向的類型派生而來的。

這裏真正的問題應該是:我知道在設計時,如何計算base指針的derived指針。如何避免運行時懲罰(dynamic_cast)?

坦率地說,我在這裏看不到一個很好的選擇,而是一種可能選項是將指針到最派生類型存儲在根類中的常​​量指針,就像這樣:

struct base { 
    void* const self; 
    virtual ~base() {} 
    protected: 
    base(void* self) : self(self) {} 
}; 
struct derived : public virtual base { 
    derived() : base(this) {} 
} 

這是醜陋而危險的,因爲它犧牲了性能的類型安全性(如果你真的很幸運,你會得到一個輕微的運行時性能)。但是您可以將reinterpret_castbase指針(self類型爲void*的成員)指定爲derived指針。

相關問題