Welcome

首页 / 软件开发 / C++ / 让TList类型安全

让TList类型安全2009-10-07在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;