wwwtc7answer.htm

来自「C++builder学习资料C++builder」· HTM 代码 · 共 162 行

HTM
162
字号


<HTML>

<HEAD>

   <TITLE> Answer What's Wrong With This Code? Volume #7</TITLE>

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

</HEAD>

<BODY>



<CENTER>

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

<TR>



<TD>







<H2>

Answer What's Wrong With This Code? Volume #7 

</H2> 

 

<H4> 

Phantom Updates 

</H4> 

<P> 

Let's show the source code for the program one more time. 

</P> 

<pre>

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

<b>__fastcall</b> TForm1<b>:</b><b>:</b>TForm1<b>(</b>TComponent<b>*</b> Owner<b>)</b>

    <b>:</b> TForm<b>(</b>Owner<b>)</b>

<b>{</b>

<b>}</b>

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

<b>void</b> <b>__fastcall</b> TForm1<b>:</b><b>:</b>btnUpdateClick<b>(</b>TObject <b>*</b>Sender<b>)</b>

<b>{</b>

    Update<b>(</b><b>)</b><b>;</b>

<b>}</b>

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

<b>void</b> <b>__fastcall</b> TForm1<b>:</b><b>:</b>Update<b>(</b><b>)</b>

<b>{</b>

    <font color="navy">// update database</font>

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

    ShowMessage<b>(</b><font color="blue">&quot;Database Updated!&quot;</font><b>)</b><b>;</b>

<b>}</b>

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

</pre> 

<P> 

The best way to debug a problem like this is to use the Call Stack viewer. We know that moving the splitter bar 

is somehow calling our database update routine. If we put a breakpoint in the <TT>Update</TT> function, we can inspect 

the call stack at the moment <TT>Update</TT> was called. This is exactly how I debugged the program originally. I put a 

breakpoint in <TT>Update</TT>, ran the program, opened the Call Stack viewer, and then moved the splitter bar. 

Moving the splitter bar trips the the breakpoint in <TT>Update</TT>. Figure 2 shows what the call stack looks like at this moment. 

</P> 

<br> 

<IMG SRC="images/splitterdebug.gif" alt="database form" BORDER=0 ALIGN="BOTTOM" width="342" height="324">  <BR> 

<H4>Figure 2. Debugging the phantom update using the Call Stack viewer</H4> 

 

<P> 

<TABLE WIDTH="100%"> 

<TR> 

<TD VALIGN="top"> 

<IMG SRC="images/exclamation.gif" ALT="Tip" BORDER=0 HSPACE="0" ALIGN="top" width="32" height="48"> 

</TD> 

<TD valign="top"> 

<b>Tip:</b> 

<hr size = 1> 

The Call Stack viewer displays function names and the arguments that were passed to the 

function. The first argument to a class member function is always a <TT>this</TT> 

pointer, or <TT>Self</TT> as its called in pascal. The address of the <TT>this</TT> pointer identifies 

which object is being called. Look at the call stack in Figure 2. The <TT>this</TT> 

pointer for <TT>TForm1::Update</TT> is <TT>0x00C528F4</TT>. This is the address 

of the main form. The value of the <TT>Self</TT> variable for <TT>TControl::WMLButtonUp</TT> 

is <TT>0x00C53F5C</TT>. This is the address of the splitter control. Using these 

two addresses, we can quickly look at the call stack and see that the bottom 5 function 

calls all pertain to the splitter control, and that the top line pertains to the form. 

<hr size = 1> 

</TD> 

</TR> 

</TABLE> 

 

<P> 

At the top of the call stack, we see our <TT>Update</TT> function. Look at the bottom row of the call stack, as its shown in Figure 2. 

The last function is <TT>TControl::WMLButtonUp</TT>. This is a method of the splitter control. <TT>WMLButtonUp</TT> calls <TT>DoMouseUp</TT>, and 

<TT>DoMouseUp</TT> calls <TT>MouseUp</TT>. These are also member functions of the splitter control. <TT>MouseUp</TT> calls <TT>UpdateControlSize</TT>. 

This is where things get interesting. Let's look at the code for <TT>UpdateControlSize</TT>. 

</P> 

<pre>

<b>procedure</b> TSplitter.UpdateControlSize;

<b>begin</b>

  ...

    Update;

<b>end</b>;

</pre> 

<P> 

The splitter bar executes a VCL method called <TT>Update</TT>. That's strange. <TT>Update</tt> is the same name as the function that I added to my form 

to update the database. I had forgotton that the VCL also had an <TT>Update</TT> function. The VCL <TT>Update</TT> method is introduced by <TT>TControl</TT>. 

When you move the splitter bar, the splitter component calls its own <TT>Update</TT> method. Not to worry though, because the splitter's <TT>Update</TT> 

function is separate from the <TT>Update</TT> method of my form. 

</P> 

<P> 

Let's continue moving up the call stack. What does the <TT>Update</TT> method of the splitter class do? The splitter control relies on the functionality 

provided in <TT>TControl::Update</TT>. The <TT>Update</TT> method of <TT>TControl</TT> looks like this: 

</P> 

<pre>

<b>procedure</b> TControl.Update;

<b>begin</b>

  <b>if</b> Parent &lt;&gt; <b>nil</b> <b>then</b> Parent.Update;

<b>end</b>;

</pre> 

<P> 

The <TT>Update</TT> method of <TT>TControl</TT> simply executes the <TT>Update</TT> method of its parent. Who is the parent of the splitter control? 

It's the form. At this point, bells should be going off in your head. When the splitter control executes the <TT>Update</TT> method of the form, it actually 

calls the database <TT>Update</TT> method of our derived form class. 

</P> 

<P> 

The problem with my code is the name that I chose for my database update function: <TT>Update</TT>. This name conflicts with the <TT>Update</tt> method of <TT>TControl</TT>. 

The help file for BCB4 says this about <TT>TControl::Update</TT>: 

</P> 

<pre>

---------------------------------------------------------------------

<B>TControl::Update</B>



Processes any pending paint messages immediately.



virtual void __fastcall Update(void);



...

---------------------------------------------------------------------

</pre> 

<P> 

Notice that the function is declared as virtual. This turns out to be the source of the problem. When you move a splitter bar, 

the splitter eventually calls the <TT>Update</TT> member function of the form. We witnessed this while debugging the program with the call stack. So which 

<TT>Update</TT> method is the splitter bar trying to call? Well, it wants to call the version of <TT>Update</TT> that has to do with painting the form. 

However, my code accidentally overrides the painting routine with a function that updates a database. When the splitter control calls the <TT>Update</TT> method of the form, 

the virtual dispatching of C++ invokes the update routine in the derived form class. Instead of repainting the form, the splitter bar updates the database! 

</P> 

<P> 

So why does the code work in C++Builder 3? Originally, I thought that maybe <TT>TControl::Update</TT> was not virtual in BCB3. This is not the case. <TT>Update</TT> 

is virtual in C++Builder 3. The difference between BCB3 and BCB4 is that <TT>TSplitter::UpdateControlSize</TT> does not call <TT>Update</TT> in BCB3. Since it does not 

call <TT>Update</TT>, the bug does not appear when compiling the code in BCB3. This does not mean that the BCB3 code is bug free. It just means that the splitter control 

will not cause the bug to surface. 

</P> 

<P> 

There are a couple of lessons to be learned here. First of all, be careful when you choose a name for a member function. 

You must be careful not to accidentally override a virtual function from a base class. The second lesson is to stay calm when a 

tester submits a bug with strange symptoms. In this edition of "What's wrong with this code", the answer to the problem was fairly simple, 

but the description of the problem was very strange. Lastly, never use the splitter control. It can be a huge source of bugs. Just kidding! 

</p> 

 

<P> 

<B>Note:</B> Here is an exercise for the reader. Would this have been a problem if I wrote my database app in Delphi instead of C++Builder? 

Would moving the splitter bar cause an update if I compiled with Delphi 4 or Delphi 5? Why or why not? 

</P> 

 

 

</TD> </TR> 

 

</TABLE> 

</CENTER> 

</BODY> 

</HTML> 

⌨️ 快捷键说明

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