In the VCL contains a TLIST class, I believe that many friends have used it, it can easily maintain the object pointer, so many friends like to use it.
To implement an array of controls. Unfortunately, this TLIST class has some problems, the most important of which is the lack of type of security support.
This article describes how to derive a new class from TLIST to implement type security, and automatically delete the object pointer.
TLIST problem
For the convenience of TLIST, there is not much to say, let's take a look, it exists, in the classs.hpp file, we can see the prototype of the function is this:
INT __FASTCALL ADD (Void * ITEM);
The compiler can convert any type of pointer to a VOID * type so that the add function can receive any type of object pointer, which is coming, if you only maintain a type of pointer may still see the potential of the problem Below we will explain its problem with an example. Suppose you want to maintain a TButton pointer, and TList can certainly complete such a job but he will not do any type check Make sure you add a TButton * pointer with the Add function.
TLIST * buttonList = new TList; // Create a Button List
ButtonList-> Add (Button1); // Add Object Pointer ButtonList-> Add (Button2); // ButtonList-> Add (New Tbutton (this)); // Ok So Far
ButtonList-> Add (Application); // Application is not ButtonButtonList-> Add (Form1); // Form1 is not ButtonList-> Add ((void *) 534); ButtonList-> Add (screen);
The above code can be operated by compilation because TLIST can receive any type of pointer.
When you try to quote the pointer, the real problem is coming:
TList * ButtonList = New TList; ButtonList-> Add (Button1); ButtonList-> Add (Button2); ButtonList-> Add (Application);
TButton * Button = ReinterPret_cast
Delete buttonlist;
I believe that you have seen the problem. When you need to get a pointer reference, TLIST doesn't know what type of pointer, so you need to convert, but who can ensure that ButtonList must be the Button pointer? You may immediately think of using Dynamic_cast to transform.
Unfortunately coming again, Dynamic_cast cannot complete this job, because the Void type pointer does not contain any type information, which means that you can't use this method, the compiler does not allow you to do this.
Dynamic_cast cannot be used, our only way is to use ReinterPret_cast, but this operator does not have any difference with the forced type conversion of previous C, which never fails, you can convert any any pointer. This way you have no way to know if there is really the Button pointer we need. In the above code snippet, the problem is not very serious. We try to convert the pointer is Application. When we change the CAPTION property, the CAPTION property of the title bar is changed, but if we try to convert the object without the corresponding properties ? The second question of TLIST is that it automatically deletes the function of the object pointer. When we destructure TLIST, it does not automatically release the object of the maintenance pointer array, and many times we need to use the manual method to complete such a thing. The following code snippet shows how to release them:
TList * ButtonList = New Tlist; // Create a list of button
ButtonList-> add (new tbutton (have); // add some buttons to the listbuttonlist-> add (new tbutton (handle)); buttonList-> add (new TButton (Handle)); ButtonList-> Add (New Tbutton (Handle);
......
Int ncount = buttonlist-> count; for (int J = 0; j
Delete buttonlist;
(Translation: There is a problem with the code above, it should be for (int J = ncount-1; j> = 0; j -), and in turn, AV may occur, otherwise AV)
On the surface, the above code can work very well, but if you think deeply, you will find a potential problem. Items [j] returns a Void pointer, which delete statement will delete the Void pointer, but delete the Void pointer and the delete TButton pointer is very different, delete the VOID pointer does not call the monograph of the object, which exists The release of the memory in the parser does not have the opportunity to execute, which will cause internal leakage.
After you can completely remove the object pointer, you must let the compiler know what class is to call the corresponding destructor. Fortunately, the destructor of the VCL is virtual, you can securely delete derived classes by converting to base classes. For example, if your List contains Button and ComboBox, you can convert them to Tcomponent, TControl, TWINControl to securely delete them, the sample code is as follows:
TList * controlist = new TLIST
Controllist-> add (new tbutton (handle); controlist-> add (new tedit (handle)); ControlList-> Add (New TcomboboX (Handle));
Int ncount = controlist-> count; for (int j = ncount; j> = 0; j -) delete reinterpret_cast
DELETE control;
The above code can safely delete any subclasses derived from TwinControl, but if is TDATSET? TDataSet is not inherited from TwinControl so Delete will call the destructor of TwinControl, which may also cause errors at runtime. Improve TLIST
Through the above discussion, we have probably understand how TLIST needs to improve. If TLIST knows the type of object it handles, most of the questions is solved. The following code is to write for this goal:
#ifndef TTYPEDLIST_H # Define TTYPEDLIST_H
#include
TEMPLATE
Void __fastcall put (int index, t * item) {TList :: Put (index, item);
Public: __fastcall ttypedlist (Bool BfreeObjects = false): TList (), bautodelete (bfreeObjects) {}
// Note: There is no destructor, call Delete directly to release memory // and Clean is virtual, you know what to do?
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 T * __fastcall first (void) {return (t *) TList :: first (); INT __FASTCALL INDEXOF (T * ITEM) {Return TList :: Indexof (item); Void __fastcall INSERT (INDEX, T * ITEM) {TList :: Insert (ITEX, ITEM); T * __fastcall last (void) {RETURN (T *) TList :: last ();} INT __FASTCALL REMOVE (INT NINDEX = TList :: Remove (item); // If we will automatically remove item if (Bautode && (Nindex! = -1)) delete item; return nindex; } __Property T * Items [int index] = {read = get, write = put}; #ENDIF Instance code // ---------------------------------------------- ---------------------------- // Sample Code 1 #incade "typedlist.h" Void __fastcall tform1 :: createButtons () {// false, not automatically delete TTYPEDLIST ButtonList-> Add (New Tbutton (this)); ButtonList-> Add (New TButton (this)); ButtonList-> Add (New Tbutton (this)); // ButtonList-> Add (application); << - Unable to compile For (int J = 0; j DELETE buttonList;} / / -------------------------------------------------------------------------------------------- ---------------------------- // instance code 2 #incade "typedlist.h" Void __fastcall tform1 :: createbuttons () {typedef TTYPEDLIST TButtonList * ButtonList (new TBUTTONLIST (TRUE); ButtonList-> Add (New Tbutton (this)); ... delete buttonlist; / / -------------------------------------------------------------------------------------------- ---------------------------- // Code Example 3: a list of tables and queries #incade "typedlist.h" Void __fastcall tform1 :: OpenDataSets () {typedef TTYPEDLIST TDataSetList * list = new tdatasetlist (false); list-> add (table1); list-> add (table2); list-> add (query3); for (int J = 0; J); DELETE LIST; By using template technology, we eliminate problems in compilation period, and also provide automatic deletion mechanism, and because of the above code uses inline technology, there is no sacrificing code efficiency. I suggest you use STL Through the above code, many beginners may be afraid, there is no type of security, no automatic deletion mechanism, change the code is so trouble, is there a simpler way? The answer is STL, STL is light, highly elastic, highly reused, and type security. If you use STL, the tList's alternative is vector.