📄 ch05.htm
字号:
when running this program.<BR>
</I><BR>
You can
find the Optimizations settings in the Project | Options | Compiler menu
option.<BR>
<BR>
<A NAME="Heading11"></A><FONT COLOR="#000077"><B>Listing 5.1. The SIMPEXP program
demonstrates basic techniques for handling exceptions.</B></FONT></P>
<PRE><FONT COLOR="#0066FF">///////////////////////////////////////
// Copyright (c) 1997 by Charlie Calvert
//
#include <vcl.h>
#pragma hdrstop
#include "Main.h"
#include "codebox.h"
#pragma resource "*.dfm"
TForm1 *Form1;
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
void __fastcall TForm1::DelExceptClick(
TObject *Sender)
{
int i = 4, j = 0, k;
k = i / j;
ShowMessage(k);
}
void __fastcall
TForm1::UserExceptClick(TObject *Sender)
{
int i = 4, j = 0, k;
try
{
k = i / j;
ShowMessage(k);
}
catch(EDivByZero &E)
{
MessageDlg("Awe Shucks! Another division by zero error!",
mtError, TMsgDlgButtons() << mbOK, 0);
}
}
void __fastcall TForm1::DeclareClick(TObject *Sender)
{
int i;
AnsiString S;
try
{
i = StrToInt("Sam");
ShowMessage(i);
}
catch(EConvertError &E)
{
AnsiString S("Class where error occurred: " + this->ClassName());
AnsiString S1("Type of error: " + E.ClassName());
MessageDlg(S + `\r' + S1 + `\r' + E.Message, mtError,
TMsgDlgButtons() << mbOK, 0);
}
}
// Address2 String is in the CodeBox unit that ships w/ the book's CD
// in the UTILS subdirectory
void __fastcall TForm1::CalcAddClick(
TObject *Sender)
{
float k;
try
{
k = StrToFloat("Sammy");
ShowMessage(k);
}
catch(EConvertError &E)
{
MessageDlg(E.Message + `\r' +
" Error Address: " + Address2Str(ExceptAddr),
mtError, TMsgDlgButtons() << mbOK, 0);
}
}
</FONT></PRE>
<P>This program contains four
<TT>TBitBtns</TT> that, when pressed, throw exceptions.
In the first case, the code lets BCB's built-in routines handle the exception; in
the rest of the examples, a custom error handler is invoked.</P>
<P>Here is the simplest way to throw and handle
an exception:</P>
<PRE><FONT COLOR="#0066FF">void __fastcall TForm1::DelExceptClick(
TObject *Sender)
{
int i = 4, j = 0, k;
k = i / j;
ShowMessage(k);
}
</FONT></PRE>
<P>The code shown here causes a divide-by-zero error, and it will
automatically pop
up the dialog shown in Figure 5.2. Notice that the <TT>ShowMessage</TT> code never
gets executed, because the exception occurs before you reach that part of your code.
In this case, it would not cause a serious problem if
<TT>ShowMessage</TT> were executed,
but you don't want it to execute because it would report nonsense to the user. The
benefit this code provides for you is therefore twofold:
<UL>
<LI>It automatically reports a reasonable error.
<P>
<LI>It saves
you the trouble of writing error-handling code that would ensure that
<TT>ShowMessage</TT> was not executed in case of an error.
</UL>
<H6></H6>
<P><A NAME="Heading12"></A><A HREF="05ebu02.jpg" tppabs="http://pbs.mcp.com/ebooks/0672310228/art/05/05ebu02.jpg">FIGURE 5.2.</A><FONT COLOR="#000077">
</FONT><I>The default mechanism for handling a divide-by-zero error.<BR>
</I><BR>
The error message shown in Figure 5.2 is useful to programmers because it provides
an address you can use in the Search | Find Error menu choice. However, it is not
a
particularly friendly message to send to a user of your program.</P>
<P>The following code demonstrates how to set up a <TT>try..catch</TT> block that
gives you a place to handle an error:</P>
<PRE><FONT COLOR="#0066FF">void __fastcall
TForm1::UserExceptClick(TObject *Sender)
{
int i = 4, j = 0, k;
try
{
k = i / j;
ShowMessage(k);
}
catch(EDivByZero &E)
{
MessageDlg("Awe Shucks! Another division by zero error!",
mtError, TMsgDlgButtons() << mbOK, 0);
}
}
</FONT></PRE>
<P>The code that you want to test appears right after the reserved word <TT>try</TT>.
In this case, all that goes on in this section is that you force a divide-by-zero
error. When the
error occurs, the code after the word <TT>catch</TT> is executed.
In this section, you designate that you want to handle <TT>EDivByZero</TT> messages
explicitly, and you do so by popping up an error message.</P>
<P>Once again, you will notice that the
<TT>ShowMessage</TT> code is never executed,
which is what you want, because it would only report nonsense to the user. Needless
to say, the size of the block of code that needs to be skipped does not matter. Nor
does it matter how deep into a series
of function calls your code might go. For instance,
instead of simply raising a divide-by-zero error on the top level, your code might
call a second function. That function could in turn call another function, and so
on, for six, ten, or however many
levels of nested calls. If any of the routines
in that block throw an <TT>EDivByZero</TT> error, the code would automatically jump
to the <TT>MessageDlg</TT> shown in the preceding code.</P>
<P>Or, to state the same fact somewhat differently, if the
code looked like this:</P>
<PRE><FONT COLOR="#0066FF">j = 0;
try
{
k = i / j;
DoSomething;
DoSomethingElse;
DoThis;
DoThat;
ShowMessage(k);
}
catch(EDivByZero &E)
{
MessageDlg("Awe Shucks! Another division by zero error!",
mtError, TMsgDlgButtons() << mbOK, 0);
}
</FONT></PRE>
<P>then <TT>DoSomething</TT>, <TT>DoSomethingElse</TT>, <TT>DoThis</TT>, and <TT>DoThat</TT>
would never be executed. Instead, the code would jump immediately from the division
statement
to the <TT>catch</TT> section.</P>
<P>You can combine the convenience of having BCB report an error with the luxury
of being able to define your own error strings:</P>
<PRE><FONT COLOR="#0066FF">void __fastcall TForm1::DeclareClick(TObject *Sender)
{
int i;
AnsiString S;
try
{
i = StrToInt("Sam");
ShowMessage(i);
}
catch(EConvertError &E)
{
AnsiString S("Class where error occurred: " + this->ClassName());
AnsiString S1("Type of
error: " + E.ClassName());
MessageDlg(S + `\r' + S1 + `\r' + E.Message, mtError,
TMsgDlgButtons() << mbOK, 0);
}
}
</FONT></PRE>
<P>The <TT>StrToInt</TT> function shown here will throw an <TT>EConvertError</TT>
because it
cannot convert the string <TT>"Sam"</TT> into an integer.</P>
<P>In this code, BCB is in effect enabling you to map an identifier onto the already
existing exception object instance that was thrown. This is not a variable declaration,
because no new storage is being allocated. It's simply giving you a convenient way
to access the exception object instance so that you can extract additional information
carried by the object instance, such as the error message string<TT>
E.Message</TT>.</P>
<P>In this case, <TT>Message</TT> returns the string that BCB would associate with
this error. To understand where <TT>Message</TT> comes from, refer to the declaration
of <TT>TException</TT> shown previously. Note also that you
need to work with the
address of the <TT>EConvertError</TT>, because this is a VCL class and must be passed
by reference:</P>
<PRE><FONT COLOR="#0066FF">catch(EConvertError &E)
</FONT></PRE>
<P>This is what you want, anyway, because it is cheaper
to pass around pointers than
to pass around an actual copy of the object. You should not, however, allocate memory
for an exception class before passing it, because that is a dangerous thing to do
during an error condition.</P>
<P>After you have
access to the <TT>message</TT> associated with an exception, you
can add your own information to it. For instance, in this case I snag the name of
the object whose method is raising the exception, as well as the name of the <TT>Exception</TT>
class
itself, thereby helping me know right away where the problem has occurred and
why:</P>
<PRE><FONT COLOR="#0066FF">AnsiString S("Class where error occurred: " + this->ClassName());
AnsiString S1("Type of error: " +
E.ClassName());
MessageDlg(S + `\r' + S1 + `\r' + E.Message, mtError,
TMsgDlgButtons() << mbOK, 0);
</FONT></PRE>
<P>The string created in these lines of code is shown in Figure 5.3.
<H6></H6>
<P><A NAME="Heading13"></A><A
HREF="05ebu03.jpg" tppabs="http://pbs.mcp.com/ebooks/0672310228/art/05/05ebu03.jpg">FIGURE 5.3.</A><FONT COLOR="#000077">
</FONT><I>An exception that shows a string created in part by BCB and in part by
the programmer. <BR>
</I><BR>
You also might want to display the actual address at which an exception is
thrown.
The VCL <TT>ExceptAddr</TT> function returns this address:</P>
<PRE><FONT COLOR="#0066FF">void __fastcall TForm1::CalcAddClick(
TObject *Sender)
{
float k;
try
{
k = StrToFloat("Sammy");
ShowMessage(k);
}
catch(EConvertError &E)
{
MessageDlg(E.Message + `\r' +
" Error Address: " + Address2Str(ExceptAddr),
mtError, TMsgDlgButtons() << mbOK, 0);
}
}
</FONT></PRE>
<P>This code displays
the regular error message associated with an <TT>EConvertError</TT>
exception, and then immediately converts the result of the <TT>ExceptAddr</TT> function
into a string so that you can see that it returns the same address displayed by a
BCB
exception. You can use this address in the Search | Find Error menu option, which
will take you to the place in your code where the error occurred.</P>
<P>To convert a pointer into a string, you can use the following function from the
<TT>CodeBox</TT>
unit:</P>
<PRE><FONT COLOR="#0066FF">AnsiString Address2Str(void *Addr)
{
return Format(`%p', OPENARRAY(TVarRec, (Addr)));
}
</FONT></PRE>
<P>This function uses the <TT>Format</TT> routine to perform a routine string operation
that returns a
string version of an address.</P>
<P>In this section, you learned how to handle simple exceptions. The actual logistics
of handling these situations can be quite complex at times, but you are now armed
with the basics needed to go into battle and wage
war with the compiler. Note that
I arranged the examples in this section in order of increasing complexity, where
the first two are more typical of the code you will use in most programs, and the
latter two are useful when you want to use advanced
techniques.
<H3><A NAME="Heading14"></A><FONT COLOR="#000077">Why Exceptions Are So Great</FONT></H3>
<P>For some reason, when I first saw exceptions, I was confused by them. The syntax
was simple enough, but I couldn't figure out exactly how I was
supposed to use the
darn things. My only consolation was that I saw so many others flounder in the same
sea.</P>
<P>If you are feeling a little confused by what you have just seen of exceptions,
try to cut through the fog created by their newness and
remember that exceptions
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -