TList对象的缺点与改进

来源:互联网 发布:施乐监控软件 编辑:程序博客网 时间:2024/05/16 09:17

在C++ Builder中,VCL库包含一个TList类,用于管理指针列表。然而,TList类含有许多缺点,其中最严重的缺点是TList缺乏类型的安全性及内存空间的自动释放。本文将探讨TList类的缺点,并提出改进方法。

一、TList的问题

1、TList主要用来存储对象的指针,使用方便,但是这个类的主要缺点是缺乏安全性,因为它存储并维护的是空指针(void *),让我们看看它的Add方法的原型:

int __fastcall Add(void * Item);

C++编译器能正确地将任何类型的指针转换为void *型指针,因此,TList的Add方法能接受你传给它的任何一个指针。这其中就产出了一个问题,当你想维护TList中的对象指针列表时,你能肯定TList中的指针具有相同类型吗?例如:

TList *ButtonList = new TList;          // create a list of buttons

 

 ButtonList->Add(Button1);             // add some buttons to the list

 ButtonList->Add(Button2);             //

 ButtonList->Add( new TButton(this));   // OK so far

 ButtonList->Add(Application);          // Application is not a button

 ButtonList->Add(Form1);              // neither is Form1

 ButtonList->Add((void *)534);   

 ButtonList->Add(Screen);

以上代码能正确编译通过,从中我们能看出,TList能处理任何对象,它在接受任何一个对象时不做任何类型检查。当你要引用TList中的一个对象时,由于它是一个空指针,你必须将它转换为一个正确类型的指针,具体方法如下:

TList *ButtonList = new TList;   

ButtonList->Add(Button1);

 … …            // 生成一个对象指针列表

TButton *button = reinterpret_cast<TButton *>(ButtonList->Items[1]);

//引用对象

button->Caption = "I hope it's really a button";   

delete ButtonList;

在以上代码中,TList的Item数组返回一个空指针(void *),要引用其中的一个TButton型的指针,必须将其转换为TButton型,这种转换的前提条件是你能肯定TList的Item返回的指针原来指向一个TButton对象。也许有个认为用dynamic_cast 操作符来完成这种转换,然而不幸的是由于空指针不含有任何类型信息,上述缺点仍然无法消除。

2、TList的另一个缺点是它不能自动删除其列表中的指针,要完全删除列表,必须遍历整个列表,将列表中的指针一个个删除。例如:

  TList *ButtonList = new TList;          // 生成一个对象列表

    ButtonList->Add(new TButton(Handle));   // 向列表中添加TButton对象

    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;

从表面上看,上述代码非常完美,但是深入一点,我们会发现一个缺点:删除列表中的某个指针时,该指针是一个空指针,而删除一个空指针与删除一个TButton型的指针是不同的,因为删除一个空指针不能自动执行一个对象的析构函数,也就不能执行析构函数中的内存释放与重新分配操作,从而会造成内存空洞。

为了能正确删除TList对象中的指针,必须将它们的类型进行转换,这样编译器才能正确调用类的析构函数。因为VCL中的所有析构函数都是虚函数,我们可以将列表中的所有转换为一个公共的基类,例如:

TList *ControlList = new TList;   

ControlList->Add(new TButton(Handle));

    ControlList->Add(new TEdit(Handle));

ControlList->Add(new TComboBox(Handle));   

int nCount = ControlList->Count;

    for (int j=0; j<nCount; j++)

        delete reinterpret_cast<TWinControl *>(ControlList->Items[j]);

delete ControlList;

若TList对象中的指针对象都是从TWinControl继承下来的,则上述代码工作正确,若某指针对象是从TDataSet继承下来的,则上述代码工作又有问题。

二、改进方法

如果TList知道它所处理的对象,那么它的许多缺点就自动消除。基于此,在TList的基础上我们引进了一个新类TTypedList,它是从TList继承下来的一个模板类,它在TList方法的基础上提供了强制类型检查,同时也提供了自动删除列表中的指针的一个方法。代码如下:

//--------------------------------------------------------------

#ifndef TTYPEDLIST_H   

#define TTYPEDLIST_H

#include <classes.hpp>

template <class T>   

class TTypedList : public TList   

{   

private:

        bool  bAutoDelete;   

protected:       

T* __fastcall Get(int Index)

{           

return (T*) TList::Get(Index);       

 }

void __fastcall Put(int Index, T* Item)       

{

TList::Put(Index,Item);       

 }    

public:

__fastcall TTypedList(bool bFreeObjects = false)         

:TList(),

bAutoDelete(bFreeObjects)       

{       

}

int __fastcall Add(T* Item)       

{           

    return TList::Add(Item);

}       

void __fastcall Delete(int Index)       

{

        if(bAutoDelete)               

            delete Get(Index);

        TList::Delete(Index);       

}       

void __fastcall Clear(void)

{           

    if(bAutoDelete)           

    {

            for (int j=0; j<Count; j++)                    

              delete Items[j];

        }

        TList::Clear();       

    }

T* __fastcall First(void)       

{           

    return (T*)TList::First();

}       

int __fastcall IndexOf(T* Item)       

{

        return TList::IndexOf(Item);        

    }

void __fastcall Insert(int Index, T* Item)       

{

        TList::Insert(Index,Item);       

}       

T* __fastcall Last(void)

{           

    return (T*) TList::Last();       

}

int __fastcall Remove(T* Item)       

{

        int nIndex = TList::Remove(Item);

        if(bAutoDelete && (nIndex != -1))               

            delete Item;

        return nIndex;       

    }

__property T* Items[int Index] = {read=Get, write=Put};   

};   

#endif

 

    最后根据改进的TTypedList类举个例了:

  在C++ Builder4中建一个新的应用程序,主要代码如下:

#include <vcl.h>

#pragma hdrstop

 

#include "Unit1.h"

#include "typedlist.h"

 

//---------------------------------------------------------------------

#pragma package(smart_init)

#pragma resource "*.dfm"

TForm1 *Form1;

//---------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner)

        : TForm(Owner)

{

}

//---------------------------------------------------------------------

 

void __fastcall TForm1::CreateButtons()

{

// Create a list of buttons.

TTypedList <TButton> *ButtonList = new TTypedList <TButton>(false);

ButtonList->Add(new TButton(this));   

ButtonList->Add(new TButton(this));

    ButtonList->Add(new TButton(this));

for (int j=0; j<ButtonList->Count; j++)   

{

        ButtonList->Items[j]->Caption = "Button" + IntToStr(j);

        ButtonList->Items[j]->Left    = 250;

        ButtonList->Items[j]->Top     = 50 + j*25;

        ButtonList->Items[j]->Parent  = this;   

}   

delete ButtonList;

  }

//-------------------------------------------------------------------------

void __fastcall TForm1::FormShow(TObject *Sender)

{

CreateButtons();

}

//-------------------------------------------------------------------------以上代码在C++ Builder5+Pwin2000 Professional环境下中通过。

0 0