2012-02-11 149 views
2

在修復巨大代碼庫中的錯誤時,我觀察到一種奇怪的情況,其中動態類型的引用從原始Derived類型更改爲Base類型!我提供了最少的代碼來說明問題:在什麼情況/情況下,dynamic_cast <>可能會失敗?

​​

funcptr是一個函數指針(void (*)(SomeClass&))。 funcptr可以指向如此多的功能,並且它們有自己的呼叫流程,因此很難調試。

很奇怪的是,在調用函數指針之後,派生類型refDerived更改爲Base。爲了緩解我的工作,我懷疑對象從Derived切片到Base,所以我製作了~Base()virtual並重新編譯了整個源代碼。但是沒有編譯器錯誤,這意味着沒有聲明Base的對象。

潛在的原因是什麼,refDerived的動態類型更改爲Base,並且dynamic_cast以後失敗?

+2

如果typeid(ref)在調用之後是基礎的,那麼函數會與對象混淆。你傳遞一個指向對象的指針,所以在整個對象上寫入隨機的東西是很容易的。 – 2012-02-11 08:54:47

+0

@LokiAstari,雖然這段代碼已經穩定好幾年了,但你告訴我的是主要的嫌疑人。 'pVoid'可能會搞亂它,但我必須看看它在什麼情況下發生。在此之前,我想檢查一下,如果我遺漏了任何可能會將動態類型更改爲靜態類型以供參考的小事。 – iammilind 2012-02-11 09:11:40

回答

2

我不相信上面的代碼是真的,因爲代碼示例沒有編譯!您不能將由dynamic_cast<Base*>(&ref)產生的Base*隱式轉換爲Derived*

這就是說,並假設typeid()的輸出實際上是正確的,有幾個可行的解釋引用改變的類型ID。所有這些都以某種形式或另一種形式指示程序中的錯誤:

  1. 被調用的函數破壞了對象,例如通過調用dynamics_cast<Base*>(obj.pVoid)->~Base()的道德等價物。
  2. 被調用的函數在obj.pVoid使用放置new指向的地址構造一個新的對象,即類似這樣的:new(obj.pVoid) Base()
  3. 某物正在覆蓋內存,導致在參考位置留下一個Base對象。
  4. 可能有更多的理由...

就個人而言,我會賭上第二種情況是情況下,即一個對象被構建到的位置。顯然,沒有看到被調用的函數,這是不可能的。

+0

這是一個錯字,我編輯過'dynamic_cast (&ref)'。原因(2)可能是不可能的,因爲我已經提到過這個問題。我已經使'Base ::〜Base()'成爲純'virtual'並重新編譯了代碼,但是沒有編譯器錯誤。這意味着沒有對象創建'Base'類。 – iammilind 2012-02-11 13:37:37

2

dynamic_cast(如果指針)可以返回0如果轉換不明確。舉例說明:

class O {…}; 
class A : public virtual O {…}; 
class B : public A {…}; 
class C : public A {…}; 
class D : public B, public C {…}; 


void f(O& p) { 
    A* const a(dynamic_cast<A*>(&p)); 
} 

void g() { 
    D d; 
    f(d); 
} 
+0

在這種情況下沒有多重繼承。此外,即使它會在那裏,在我的情況下,它不會影響。因爲在函數調用之前,'Base&ref'的動態類型是'Derived'。所以它應該保持原樣。 – iammilind 2012-02-11 09:14:17

+0

@iammilind好的。如果它幫助別人,我會放棄它。認爲:也許你可以添加一個(或多個)參數的虛擬調用,並逐步查看實際執行的內容以幫助確定類型(或者暴露內存碎片?)。取決於你的程序,它可能會導致一些線索。減少能見度(或刪除方法)也有時也有所幫助。 – justin 2012-02-11 09:42:10

0

在我的具體情況下,dynamic_cast<>失敗的原因是由於delete提前引用!

一旦指針或引用是delete +破壞,然後任何企圖將其向下到原來的類型將導致進入「未定義行爲」區。在我的情況下,dynamic_cast<>失敗。

void foo (Base &ref) 
{ 
    SomeClass obj; 
    obj.pVoid = &ref; 

    (*funcptr)(obj); // ----> delete (Base*)(obj.pVoid); in one of the callbacks 

    Derived *p = dynamic_cast<Derived*>(&ref); // fails => "p = 0" 
} 
+0

這只是UB – paulm 2014-11-13 08:42:38

+1

@paulm,是的,在這個答案中已經用粗體字提到了。 – iammilind 2014-11-14 08:50:39

相關問題