在VCL中包含有一個TList類,相信很多朋友都使用過,它可以方便的維護對象指針,所以很多朋友都喜歡用它
來實現控件數組。不幸的是,這個TList類有一些問題,其中最重要就是缺乏類型安全的支持。
這篇文章介紹如何從TList派生一個新類來實現類型安全,並且能自動刪除對象指針的方法。
TList的問題所在
對於TList的方便性這裡就不多說,我們來看一下,它到底存在什麼問題,在Classes.hpp文件中,我們可以看到函數的原型是這樣申明的:
int __fastcall Add(void * Item);
編譯器可以把任何類型的指針轉換為void*類型,這樣add函數就可以接收任何類型的對象指針,這樣問題就來了,如果你僅維護一種類型的指針也許還看不到問題的潛在性,下面我們以一個例子來說明它的問題所在。假設你想維護一個TButton指針,TList當然可以完成這樣的工作但是他不會做任何類型檢查確保你用add函數添加的一定是TButton*指針。
TList *ButtonList = new TList; // 創建一個button list
ButtonList->Add(Button1); // 添加對象指針
ButtonList->Add(Button2); //
ButtonList->Add( new TButton(this)); // OK so far
ButtonList->Add(Application); // Application不是button
ButtonList->Add(Form1); // Form1也不是
ButtonList->Add((void *)534);
ButtonList->Add(Screen);
上面的代碼可以通過編譯運行,因為TList可以接收任何類型的指針。
當你試圖引用指針的時候,真正的問題就來了:
TList *ButtonList = new TList;
ButtonList->Add(Button1);
ButtonList->Add(Button2);
ButtonList->Add(Application);
TButton *button = reinterpret_cast<TButton *>(ButtonList->Items[2]);
button->Caption = "I hope it's really a button";
delete ButtonList;
相信你已經看到了問題的所在,當你需要取得指針引用的時候TList並不知道那是個什麼類型的指針,所以你需要轉換,但誰能保證ButtonList裡一定是Button指針呢?你也許會馬上想到使用dynamic_cast來進行轉化。
不幸再次降臨,dynamic_cast無法完成這樣的工作,因為void類型的指針不包含任何類型信息,這意味著你不能使用這樣的方法,編譯器也不允許你這樣做。
dynamic_cast不能使用了,我們唯一的方法就是使用reinterpret_cast,不過這個操作符同以前c的強制類型轉換沒有任何區別,它總是不會失敗,你可以把任何在任何指針間轉換。這樣你就沒有辦法知道List中是否真的是我們需要的Button指針。在上面的代碼片斷中,問題還不是非常嚴重,我們試圖轉換的指針是Application,當我們改變Caption屬性的時候,最多把標題欄的Caption屬性改了,可是如果我們試圖轉換的對象沒有相應的屬性呢?
TList的第二個問題是,它自動刪除對象指針的功能,當我們析構TList的時候,它並不能自動釋放維護的指針數組的對象,很多時候我們需要用手工的方法來完成這樣一件事情,下面的代碼片斷顯示了如何釋放他們:
TList *ButtonList = new TList; // create a list of buttons
ButtonList->Add(new TButton(Handle)); // add some buttons to the list
ButtonList->Add(new TButton(Handle));
ButtonList->Add(new TButton(Handle));
ButtonList->Add(new TButton(Handle));
...
...
int nCount = ButtonList->Count;
for (int j=0; j<nCount; j++)
delete ButtonList->Items[j];
delete ButtonList;