2011-06-30 109 views
1

我想確定一個T *指針指向的對象是否真的是一個T對象或其他一些不相關的類型。我想dynamic_cast的,但它是小於沒用,它返回指針本身,而不是零,即使很明顯它並不指向有效的T類型的對象:dynamic_cast到相同類型不檢查對象的類型

Object* garbage = reinterpret_cast<Object*>(0x12345678); 
if(dynamic_cast<Object*>(garbage) == NULL){ 
    cout << "Expected behaviour (by me)" << endl; 
}else{ 
    cout << "You've got to be kidding me" << endl; 
} 

對此有什麼解決辦法,或一些其他方案?我試圖在void dynamic_cast之前void *和char *無效,typeid是不夠的,因爲我也想接受子類。一些上下文:我正在寫一個自定義數組類,實現不同種類的數組之間的淺轉換,如Array<Object*>Array<String*>,我希望通過在每個元素訪問時進行動態類型檢查來保證最小的類型安全性,例如:

#define DEBUG 
Array<String*> v(10); 
Array<Object*> o = v; 
o[0] = new Integer(1);  // this is technically illegal but no idea how to check 
//Array<String*> w = o;  // this fails with an exception 
String* str = v[0];   // but this should fail horribly as well 
cout << str << endl; 

鑄造到對象*,然後做類型檢查的對象*在很多情況下工作,但它在Array<Object*>的情況下失敗,但我不知道是否有可能在不使用reinterpret_cast的情況下將非對象插入Array<Object*>

+1

什麼是字符串對象和整數?爲什麼你的'String * str = v [0];'行失敗? 'v [0]'是一個'String *',所以複製是完全有效的。 –

+0

我已經在代碼中加入了一些說明。對象是String和Integer的(公共的,非虛擬的)基礎,它們都是多態的,至少有一個虛擬析構函數。對象從NonCopyable私有派生;字符串和整數從一些「接口」公開和虛擬地派生出來,但我懷疑這起到了作用。 我正在實現T * Array :: operator []以包含必要的類型檢查,以確保當我遇到dynamic_cast的奇怪行爲時它返回一個有效對象(或null)。 – Frigo

回答

1

基於你的例子,這聽起來像你有淺拷貝陣列,有人可以欺騙,包含不同的類型比他們應該包含。我認爲這個問題的「正常」解決辦法是讓用戶難以做到(即不提供Array<T>Array<U>之間的轉換)。但是,如果你在你的想法設置,我認爲這會工作:

template<typename Subclass> 
class Array { 
public: 
    // ... 
    Subclass *operator [] (size_t index) { 
     assert(index < size_); 
     assert(dynamic_cast<Subclass*>(static_cast<Object*>(internal_[index])) != NULL); 
     // ... 
    } 
    // ... 
private: 
    size_t size_; 
    Subclass **internal_; 
}; 

你可以做一些模板元魔法和靜態斷言,以確保Subclass真是Object子類(究竟是怎麼一個完全不同的話題)。一旦完成,將其轉換爲Object *,然後使用dynamic_cast備份到Subclass即可實現您的目標。

+0

這正是我想要做的。使用(可選)動態檢查轉換和元素訪問淺拷貝數組。轉換很簡單,我們只需要定義一個模板拷貝構造函數,除了使用dynamic_cast檢查的原始文件(保證兩種類型不一樣)之外,問題在於元素訪問。 我可以想到兩種方法:一種涉及operator []的代理類返回值,而不是T *&,它會針對數組的實際類型檢查所有讀取和寫入操作,而我認爲這是不可能的。 – Frigo

+0

第二種方法涉及檢查operator []本身內部的檢查,這是我遇到的問題,它不能防止非法寫入,但如果有人試圖訪問非法元素,則會識別錯誤的狀態。我最後的做法基本上和你的一樣。我不確定它的正確性,但坦率地說,除了reinterpret_cast之外,我想不出任何可以繞過它的情況。 – Frigo

+0

這怎麼能防止一個'新的整數'分配到'Array '這個恰好在那個索引處有一個'String *'?測試寫成'dynamic_cast (static_cast (internal_ [index]))',將只驗證傳入的Integer可以轉換爲Object *'而不是'String *'...否? –

0

A dynamic_cast從相同類型定義爲C++中沒有操作,所以它不能用你的例子「失敗」。您可以改用typeid運算符。

舉例來說,這個程序很可能崩潰(這是在一個隨機地址從一個對象獲取類型信息的「預期」的結果):

int main() 
{ 
    Object* garbage = reinterpret_cast<Object*>(0x12345678); 
    if (typeid(*garbage) == typeid(Object)) 
     cout << "Your program thinks this garbage is an actual object!" << std::endl; 
} 
+1

仍有可能出現誤判。 –

+1

@Ben Voigt的對象 – bdonlan

+0

的子類在type_info或崩潰的比較上會失敗嗎? – zneak

0

剛剛推出一個共同的基類與虛擬析構函數。通過空基優化,這可能不會增加任何開銷,並且它會使dynamic_cast正常工作。

1

讓我看看,如果我按照您的需求,使沿途的一些建議...

Array<String*> v(10); 

看來,這是爲了給你10 String*小號初始化的數組爲NULL/0 。

Array<Object*> o = v; 

創建的v.size()/10 Object*秒的陣列中,從每個String*s複製在v

o[0] = new Integer(1); // technically illegal but no idea how to check 

如果這是非法的,那麼你顯然要防止Object* S其中,改變了運行時類型覆蓋...

  • 你需要攔截operator=之前實行/類型比較後
  • 攔截operator=,你需要o[0]返回一個類型,其operator=您可以指定
    • o[0]返回一個對象*將永遠不會工作,因爲指針不是用戶定義的類:你不能修改operator=行爲
    • 你必須有o [0]返回一個代理對象 - 這裏幾乎是一個迭代器語義和分配類型斷言是從標準容器迭代器

不同這給我們帶來:

Array<String*> w = o;  // this fails with an exception 

我想這是唯一的失敗,因爲你的上面的3210並不是第一次失敗,而且例外是您故意測試的元素類型符合期望:如果您使用代理對象來討論阻止Integer進入Array<Object*>,那麼這裏沒有問題。

String* str = v[0];  // should fail horribly as well 

同樣,我猜,因爲你剛纔Integer任務並未就此失敗,這裏有沒有新的問題。

cout << str << endl; 

所以,代理對象似乎很關鍵。讓我知道,如果你不知道如何寫一個,但我猜你是這樣做的......

+0

是的,你的假設是完全正確的,除了複製機制。我有一個代理想法,但由於難以存儲初始類型信息而被駁回;我想到的方法是一個類型檢查基類+子類模板,其中包含檢查指向原始模板參數的指針的方法。代理將使用這個類來允許或禁止操作。 SoapBox提供的解決方案雖然易於實現。我們不檢查對象的有效性,直到我們將它們作爲某種類型的指針訪問它們。 – Frigo

+0

@Frigo:「在我們訪問之前不要檢查對象的有效性」 - 絕對是一個方便簡單的地方,可以接受:-)。 Re「正確,除了複製機制」,是爲了響應我的「每個從v中的String * s複製」?你只是意味着它做了一個很深的複製/克隆,而不是一個「簡單」的淺拷貝(我沒有把它拼出來,但它是我所假設的),還是還有更多的東西?乾杯。 –