⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 list.htm

📁 C++builder学习资料C++builder
💻 HTM
📖 第 1 页 / 共 2 页
字号:


<HTML>

<HEAD>

   <TITLE>Type-safe TList Objects.</TITLE>

   <META NAME="Author" CONTENT="Harold Howe">

</HEAD>

<BODY>



<CENTER>

<TABLE  BORDER=0 CELLPADDING=0 CELLSPACING=0 WIDTH="640">

<TR><TD>

<H2>

Type-safe <TT>TList</TT> Objects.

</H2>



<P>

The VCL contains a class called <TT>TList</TT> that provides an easy way to maintain a list of pointers. Unfortunately,

<TT>TList</TT> has a handful of serious shortcomings. The most serious shortcoming is the lack of type safety.

</P>



<P>

This article demonstrates a type-safe derivative of <TT>TList</TT>. The derived class addresses some of the

defincicies of <TT>TList</TT> by providing type safety and automatic deletion of the objects contained in the list.

</P>

<UL>

<LI><A HREF="#explanation">The problem with TList</A>

<LI><A HREF="#list"       >An improved list class</A>

<LI><A HREF="#code"       >Code examples</A>

<LI><A HREF="#notes"      >Notes</A>

</UL>

</P>

<BR>

<H3>

<A NAME="explanation">The problem with <TT>TList</TT></A>

</H3>

<P>

<TT>TList</TT> is a handy class for storing a list of pointers to objects, but the class has some problems. The main

problem with <TT>TList</TT> is that the class is not type-safe. <TT>TList</TT> maintains the list as a set of void

pointers. Take a look at the prototype for the <TT>Add</TT> method of <TT>TList</TT> in <TT>CLASSES.HPP</TT>. It looks

something like this:

</P>

<pre>

<b>int</b> <b>__fastcall</b> Add<b>(</b><b>void</b> <b>*</b> Item<b>)</b><b>;</b>

</pre>

<P>

The compiler can implicitly convert any pointer to a void pointer. As a result, the <TT>Add</TT> function will accept

any pointer that you pass it. This presents a problem when you want to maintain a list of objects that are the same

type. The following code example illustrates the problem. Imagine that you want to maintain a list of <TT>TButton</TT>

pointers. <TT>TList</TT> can handle the job, but it doesn't perform any type checking to ensure that only button

objects are added to the list.

</P>

<pre>

TList <b>*</b>ButtonList <b>=</b> <b>new</b> TList<b>;</b>        <font color="navy">// create a list of buttons</font>



ButtonList<b>-></b>Add<b>(</b>Button1<b>)</b><b>;</b>             <font color="navy">// add some buttons to the list</font>

ButtonList<b>-></b>Add<b>(</b>Button2<b>)</b><b>;</b>             <font color="navy">//</font>

ButtonList<b>-></b>Add<b>(</b> <b>new</b> TButton<b>(</b><b>this</b><b>)</b><b>)</b><b>;</b>  <font color="navy">// OK so far</font>



ButtonList<b>-></b>Add<b>(</b>Application<b>)</b><b>;</b>         <font color="navy">// Application is not a button</font>

ButtonList<b>-></b>Add<b>(</b>Form1<b>)</b><b>;</b>               <font color="navy">// neither is Form1</font>

ButtonList<b>-></b>Add<b>(</b><b>(</b><b>void</b> <b>*</b><b>)</b><font color="blue">534</font><b>)</b><b>;</b>

ButtonList<b>-></b>Add<b>(</b>Screen<b>)</b><b>;</b>

</pre>

<P>

The code example above compiles and runs without a hitch. <TT>TList</TT> does nothing to enforce what types of objects

are added to the list. Any pointer type will be accepted.

</P>

<P>

The type safety issue becomes more serious when you start dereferencing the objects in the list. Because the list

contains void pointers, you must cast the elements from the list to their proper types. The code example below

shows how that is typically done:

</P>

<pre>

TList <b>*</b>ButtonList <b>=</b> <b>new</b> TList<b>;</b>

ButtonList<b>-></b>Add<b>(</b>Button1<b>)</b><b>;</b>

ButtonList<b>-></b>Add<b>(</b>Button2<b>)</b><b>;</b>

ButtonList<b>-></b>Add<b>(</b>Application<b>)</b><b>;</b>



TButton <b>*</b>button <b>=</b> <b>reinterpret_cast</b>&lt;TButton <b>*</b><b>></b><b>(</b>ButtonList<b>-></b>Items<b>[</b><font color="blue">1</font><b>]</b><b>)</b><b>;</b>

button<b>-></b><b>></b>Caption <b>=</b> <font color="blue">"I hope it's really a button"</font><b>;</b>



<b>delete</b> ButtonList<b>;</b>

</pre>

<P>

The <TT>Items</TT> array property of <TT>TList</TT> returns a void pointer. If you are trying to maintain a list of

<TT>TButton</TT> pointers, then a value from the <TT>Items</TT> array will need to be cast to a <TT>TButton</TT>

pointer. This presents a problem. You must assume that the returned item is a button. There is no way to ask the object

if it really is a button. You may be inclined to use the type safe <TT>dynamic_cast</TT> operator to perform the cast.

Unfortunately, that won't work. Void pointers don't maintain any type information, which means that you can't use

<TT>dynamic_cast</TT> to cast a void pointer. The compiler won't allow you to.

</P>

<P>

Since <TT>dynamic_cast</TT> can't be used, the only way to convert the void pointer is to use <TT>reinterpret_cast</TT>.

Unfortunately, <TT>reinterpret_cast</TT> is no different than the old C style of casting. <TT>reinterpret_cast</TT> never

fails, which allows you cast a pointer to any other type of pointer. As a result, you have no way of knowing if the

item from the list is really the type of object you expect it to be. In the previous code segment, we took the second

button in the list and changed its <TT>Caption</TT> property. What if we tried to do that with the third item in the

list, which isn't really a <TT>TButton</TT> object? The code would still work, but instead of changing the caption of

a button, the caption of the program's taskbar icon would change.

</P>

<P>

Another problem with <TT>TList</TT> is that it doesn't automatically delete the pointers that are contained in the list.

This is a feature that would sometimes come in handy. In order to properly clean up the list, it is necessary to loop

through the list and delete each object. Here is some code that attempts to deallocate the pointers:

</P>

<pre>

TList <b>*</b>ButtonList <b>=</b> <b>new</b> TList<b>;</b>          <font color="navy">// create a list of buttons</font>



ButtonList<b>-></b>Add<b>(</b><b>new</b> TButton<b>(</b>Handle<b>)</b><b>)</b><b>;</b>   <font color="navy">// add some buttons to the list</font>

ButtonList<b>-></b>Add<b>(</b><b>new</b> TButton<b>(</b>Handle<b>)</b><b>)</b><b>;</b>

ButtonList<b>-></b>Add<b>(</b><b>new</b> TButton<b>(</b>Handle<b>)</b><b>)</b><b>;</b>

ButtonList<b>-></b>Add<b>(</b><b>new</b> TButton<b>(</b>Handle<b>)</b><b>)</b><b>;</b>



<b>...</b>

<b>...</b>



<b>int</b> nCount <b>=</b> ButtonList<b>-></b>Count<b>;</b>

<b>for</b> <b>(</b><b>int</b> j<b>=</b><font color="blue">0</font><b>;</b> j&lt;nCount<b>;</b> j<b>++</b><b>)</b>

    <b>delete</b> ButtonList<b>-></b>Items<b>[</b>j<b>]</b><b>;</b>



<b>delete</b> ButtonList<b>;</b>



</pre>

<P>

On the surface, this code seems just fine. But dig a little deeper and you will find a sinistair flaw. Focus on the

line that deletes an item from the list. Since <TT>Items[j]</TT> returns a void pointer, the delete statement

deletes a void pointer. Deleting a void pointer is vastly different than deleting a <TT>TButton</TT> pointer.

Deleting a void pointer does not execute the objects destructor. As a result, any deallocation that the class does in

its destructor will not occur, which causes a memory leak.

</P>

<P>

In order to properly delete objects from the list, you must cast them so the compiler knows that it should call the

destructor of the class. Since all destructors in the VCL are virtual, you can survive by casting the items to a common

base. For example, if the list contains buttons and combo boxes, you can delete the items by casting them to a

<TT>TComponent</TT>, <TT>TControl</TT>, or <TT>TWinControl</TT>. Here is a code example:

</P>

<pre>

TList <b>*</b>ControlList <b>=</b> <b>new</b> TList<b>;</b>



ControlList<b>-></b>Add<b>(</b><b>new</b> TButton<b>(</b>Handle<b>)</b><b>)</b><b>;</b>

ControlList<b>-></b>Add<b>(</b><b>new</b> TEdit<b>(</b>Handle<b>)</b><b>)</b><b>;</b>

ControlList<b>-></b>Add<b>(</b><b>new</b> TComboBox<b>(</b>Handle<b>)</b><b>)</b><b>;</b>



<b>int</b> nCount <b>=</b> ControlList<b>-></b>Count<b>;</b>

<b>for</b> <b>(</b><b>int</b> j<b>=</b><font color="blue">0</font><b>;</b> j&lt;nCount<b>;</b> j<b>++</b><b>)</b>

    <b>delete</b> <b>reinterpret_cast</b>&lt;TWinControl <b>*</b><b>></b><b>(</b>ControlList<b>-></b>Items<b>[</b>j<b>]</b><b>)</b><b>;</b>



<b>delete</b> ControlList<b>;</b>

</pre>

<P>

This code will properly delete any object in the list that is derived from <TT>TWinControl</TT>, but what happens if

someone sneaks a <TT>TDataSet</TT> pointer into the list? <TT>TDataSet</TT> is not derived from <TT>TWinControl</TT>.

The code will delete the dataset by calling the destructor of <TT>TWinControl</TT>. This will certainly cause run time

havoc.

</P>

<P>

Lastly, <TT>TList</TT> could make life easy if it provided an option for automatically deleting the pointers in the

list when we delete the list itself. But, since <TT>TList</TT> internally maintains a list of void pointers, we now know that

it couldn't delete the objects properly anyway.

</P>

<BR>

<H3>

<A NAME="list">An improved list class</A>

</H3>

<P>

The problems outlined in the previous section demonstrate the need for an improved <TT>TList</TT>. If <TT>TList</TT>

knew what types of objects it was dealing with, most of its problems would disappear. The code listing below provides a

new class that solves many of the problems introduced by <TT>TList</TT>. The class is a template class derived from

<TT>TList</TT>. The code works by providing a type safe layer around the existing methods of <TT>TList</TT>. These

type safe wrappers allow the compiler to enforce type safety at compile time. The class also provides an option for

automatically deleting the pointers in the list when the list itself is deleted.

</P>

<pre>

<font color="navy">//--------------------------------------------------------------</font>

<font color="green">#ifndef TTYPEDLIST_H</font>

<font color="green">#define TTYPEDLIST_H</font>



<font color="green">#include &lt;classes.hpp></font>



<b>template</b> &lt;<b>class</b> T<b>></b>

<b>class</b> TTypedList <b>:</b> <b>public</b> TList

<b>{</b>

<b>private</b><b>:</b>

    <b>bool</b> bAutoDelete<b>;</b>

<b>protected</b><b>:</b>

    T<b>*</b> <b>__fastcall</b> Get<b>(</b><b>int</b> Index<b>)</b>

    <b>{</b>

        <b>return</b> <b>(</b>T<b>*</b><b>)</b> TList<b>:</b><b>:</b>Get<b>(</b>Index<b>)</b><b>;</b>

    <b>}</b>



    <b>void</b> <b>__fastcall</b> Put<b>(</b><b>int</b> Index<b>,</b> T<b>*</b> Item<b>)</b>

    <b>{</b>

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -