?? ch26.htm
字號:
int* retval = AllocateBuffer();
delete floatarray;
return retval;
}
int* AllocateBuffer()
{
int* buffer = new int[256];
if (buffer == NULL)
{
MyException* exception =
new MyException("Memory allocation failed!");
throw exception;
}
return buffer;
</PRE>
<PRE>}
</PRE>
<P>If the exception is thrown, execution of AllocateBuffer() is abandoned immediately.
The stack unwinds. Because there is no catch block in Intermediate(), execution of
that function will be abandoned after the call to AllocateBuffer(). The delete for
floatarray will not happen, but the destructor for bigarray will be executed. Listing
26.5 shows a way around this problem.</P>
<P>
<H4>Listing 26.5  Rethrowing Exceptions</H4>
<PRE>int* Intermediate()
{
BigObject bigarray;
float* floatarray = new float[1000];
int* retval = NULL;
try
{
retval = AllocateBuffer();
}
catch (MyException e)
{
delete floatarray;
throw;
}
delete floatarray;
return retval;
</PRE>
<PRE>}
</PRE>
<P>This revised version of Intermediate() catches the exception so that it can delete
floatarray and throw it farther up to the calling function. (Notice that the name
of the exception is not in this throw statement; it can throw only the exception
it just caught.) There are a few things you should notice about this revised code:</P>
<P>
<UL>
<LI>The line that deletes floatarray has been duplicated.
<P>
<LI>The declaration of retval has had to move out of the try block so that it will
still be in scope after the try block.
<P>
<LI>retval has been initialized to a default value.
</UL>
<P>This is really starting to get ugly. Through this entire process, the BigObject
called bigarray has been quietly handled properly and easily, with an automatic call
to the destructor no matter which function allocated it or where the exception was
called. When you write code that uses exceptions, wrapping all your heap-allocated
objects in classes such as BigObject makes your life easier. BigObject uses a <I>managed
pointer</I>: When a BigObject object such as bigarray goes out of scope, the memory
it pointed to is deleted. A very flexible approach to managed pointers is described
at the end of the section on templates in this chapter.</P>
<P>
<H3><A NAME="Heading5"></A>Handling Multiple Types of Exceptions</H3>
<P>Often, a block of code generates more than one type of exception, so you may want
to use multiple catch blocks with a try block. You might, for example, need to be
on the lookout for both CException and char* exceptions. Because a catch block must
receive a specific type of exception object, you need two different catch blocks
to watch for both CException and char* exception objects. You can also set up a catch
block to catch whatever type of exception hasn't been caught yet, by placing ellipses
(...) in the parentheses, rather than a specific argument. The problem with this
sort of multipurpose catch block is that you have no access to the exception object
received and so must handle the exception in a general way.</P>
<P>Listing 26.6 is a program that generates three different types of exceptions based
on a user's input. (In a real program, you shouldn't use exceptions to deal with
user errors. It's a slow mechanism, and checking what the user typed can usually
be handled more efficiently in another way.)</P>
<P>Running the program, you're instructed to enter a value between 4 and 8, except
for 6. If you enter a value less than 4, the program throws a MyException exception;
if you enter a value greater than 8, the program throws a char* exception; and, finally,
if you happen to enter 6, the program throws the entered value as an exception.</P>
<P>Although the program throws the exceptions in the GetValue() function, the program
catches them all in main(). The try block in main() is associated with three catch
blocks. The first catches the MyException object, the second catches the char* object,
and the third catches any other exception that happens to come down the pike.</P>
<BLOCKQUOTE>
<P>
<HR>
<strong>NOTE:</strong> Similar to if...else statements, the order in which you place catch
program blocks can have a profound effect on program execution. You should always
place the most specific catch blocks first. For example, in Listing 26.6, if the
catch(...) block were first, none of the other catch blocks would ever be called.
This is because the catch(...) is as general as you can get, catching every single
exception that the program throws. In this case (as in most cases), you want to use
catch(...) to receive only the leftover exceptions. 
<HR>
</BLOCKQUOTE>
<H4>Listing 26.6  EXCEPTION6.CPP--Using Multiple catch Blocks</H4>
<PRE>#include <iostream.h>
class MyException
{
protected:
char* m_msg;
public:
MyException(char *msg) { m_msg = msg;}
~MyException(){}
char* GetError() {return m_msg;}
};
int GetValue();
int main()
{
try
{
int value = GetValue();
cout << "The value you entered is okay." << endl;
}
catch(MyException* exception)
{
char* msg = exception->GetError();
cout << msg << endl;
}
catch(char* msg)
{
cout << msg << endl;
}
catch(...)
{
cout << "Caught unknown exception!" << endl;
}
return 0;
}
int GetValue(){
int value;
cout << "Type a number from 4 to 8 (except 6):" << endl;
cin >> value;
if (value < 4)
{
MyException* exception =
new MyException("Value less than 4!");
throw exception;
}
else if (value > 8)
{
throw "Value greater than 8!";
}
else if (value == 6)
{
throw value;
}
return value;
</PRE>
<PRE>}
</PRE>
<H3><A NAME="Heading6"></A>The Old Exception Mechanism</H3>
<P>Before try, catch, and throw were added to Visual C++, there was a rudimentary
form of exception handling available to both C and C++ programmers through macros
called TRY, CATCH, and THROW. These macros are a little slower than the standard
exception mechanisms and can throw only exceptions that are objects of a class derived
from CException. Don't use these in your programs. If you have an existing program
that uses them, you may want to convert to the new mechanism. There's a helpful article
on this topic in the Visual C++ documentation: search for TRY and you'll find it.</P>
<P>
<H2><A NAME="Heading7"></A>Exploring Templates</H2>
<P>It's a good guess that, at one time or another, you wished you could develop a
single function or class that could handle any kind of data. Sure, you can use function
overloading to write several versions of a function, or you can use inheritance to
derive several different classes from a base class. But, in these cases, you still
end up writing many different functions or classes. If only there were a way to make
functions and classes a little smarter so that you could write just one function
that handled any kind of data you wanted to throw at it. There is a way to accomplish
this seemingly impossible task. You need to use something called <I>templates,</I>
the focus of this section.</P>
<P>
<H3><A NAME="Heading8"></A>Introducing Templates</H3>
<P>A <I>template</I> is a kind of blueprint for a function or class. You write the
template in a general way, supplying placeholders, called <I>parameters</I>, for
the data objects that the final function or class will manipulate. A template always
begins with the keyword template followed by a list of parameters between angle brackets,
like this:</P>
<P>
<PRE>template<class Type>
</PRE>
<P>You can have as many parameters as you need, and you can name them whatever you
like, but each must begin with the class keyword and must be separated by commas,
like this:</P>
<P>
<PRE>template<class Type1, class Type2, class Type3>
</PRE>
<P>As you may have guessed from previous discussion, there are two types of templates:
function and class. The following sections describe how to create and use both types
of templates.</P>
<P>
<H3><A NAME="Heading9"></A>Creating Function Templates</H3>
<P>A function template starts with the template line you just learned about, followed
by the function's declaration, as shown in Listing 26.7. The template line specifies
the types of arguments that will be used when calling the function, whereas the function's
declaration specifies how those arguments are to be received as parameters by the
function. Every parameter specified in the template line must be used by the function
declaration. Notice the Type1 immediately before the function name. Type1 is a placeholder
for the function's return type, which will vary, depending on how the template is
used.</P>
<P>
<H4>Listing 26.7  The Basic Form of a Function Template</H4>
<PRE>template<class Type1, class Type2>
Type1 MyFunction(Type1 data1, Type1 data2, Type2 data3)
{
// Place the body of the function here.
</PRE>
<PRE>}
</PRE>
<P>An actual working example will help you understand how function templates become
functions. A common example is a Min() function that can accept any type of arguments.
Listing 26.8 is a short program that defines a template for a Min() function and
then uses that function in main(). When you run the program, the program displays
the smallest value of whatever data is sent as arguments to Min(). This is possible
because the compiler takes the template and creates functions for each of the data
types that are compared in the program.</P>
<P>
<H4>Listing 26.8  TEMPLATE1.CPP--Using a Typical Function Template</H4>
<PRE>#include <iostream.h>
template<class Type>
Type Min(Type arg1, Type arg2)
{
Type min;
if (arg1 < arg2)
min = arg1;
else
min = arg2;
return min;
}
int main()
{
cout << Min(15, 25) << endl;
cout << Min(254.78, 12.983) << endl;
cout << Min(`A', `Z') << endl;
return 0;
</PRE>
<PRE>}
</PRE>
<BLOCKQUOTE>
<P>
<HR>
<strong>NOTE:</strong>otice how, in Listing 26.8, the Min() template uses the data type
Type not only in its parameter list and function argument list but also in the body
of the function in order to declare a local variable. This illustrates how you can
use the parameter types just as you would use any specific data type such as int
or char. 
<HR>
</BLOCKQUOTE>
<P>Because function templates are so flexible, they often lead to trouble. For example,
in the Min() template, you have to be sure that the data types you supply as parameters
can be compared. If you tried to compare two classes, your program would not compile
unless the classes overloaded the < and > operators.</P>
<P>Another way you can run into trouble is when the arguments you supply to the template
are not used as you think. For example, what about adding the following line to main()
in Listing 26.6?</P>
<P>
<PRE>cout << Min("APPLE", "ORANGE") << endl;
</PRE>
<P>If you don't think about what you're doing in the previous line, you may jump
to the conclusion that the returned result will be APPLE. The truth is that the preceding
line may or may not give you the result you expect. Why? Because the "APPLE"
and "ORANGE" string constants result in pointers to char. This means that
the program will compile smoothly, with the compiler creating a version of Min()
that compares char pointers. But there's a big difference between comparing two pointers
and comparing the data to which the pointers point. If "ORANGE" happens
to be stored at a lower address than "APPLE", the preceding call to Min()
results in "ORANGE".</P>
<P>A way to avoid this problem is to provide a specific replacement function for
Min() that defines exactly how you want the two string constants compared. When you
provide a specific function, the compiler uses that function rather than create one
from the template. Listing 26.9 is a short program that demonstrates this important
technique. When the program needs to compare the two strings, it doesn't call a function
created from the template but instead uses the specific replacement function.</P>
<P>
<H4>Listing 26.9  TEMPLATE2.CPP--Using a Specific Replacement Function</H4>
<PRE>#include <iostream.h>
#include <string.h>
template<class Type>
Type Min(Type arg1, Type arg2)
{
Type min;
if (arg1 < arg2)
min = arg1;
else
min = arg2;
return min;
}
char* Min(char* arg1, char* arg2)
{
char* min;
int result = strcmp(arg1, arg2);
if (result < 0)
min = arg1;
else
min = arg2;
return min;
}
int main()
{
cout << Min(15, 25) << endl;
cout << Min(254.78, 12.983) << endl;
cout << Min(`A', `Z') << endl;
cout << Min("APPLE", "ORANGE") << endl;
return 0;
</PRE>
<PRE>}
</PRE>
<H3><A NAME="Heading10"></A>Creating Class Templates</H3>
<P>Just as you can create abstract functions with function templates, so too can
you create abstract classes with class templates. A class template represents a class,
which in turn represents an object. When you define a class template, the compiler
takes the template and creates a class. You then declare (instantiate) objects of
the class. As you can see, class templates add another layer of abstraction to the
concept of classes.</P>
<P>You define a class template much as you define a function template--by supplying
the template line followed by the class's declaration, as shown in Listing 26.10.
Notice that, just as with a function template, you use the abstract data types given
as parameters in the template line in the class's body. They might be the types for
member variables, return types, and other data objects.</P>
<P>
<H4>Listing 26.10  Defining a Class Template</H4>
<PRE>template<class Type>
class CMyClass
{
protected:
Type data;
public:
CMyClass(Type arg) { data = arg; }
~CMyClass() {};
</PRE>
<PRE>};
</PRE>
<P>When ready to instantiate objects from the template class, you must supply the
data type that will replace the template parameters. For example, to create an object
of the CMyClass class, you might use a line like this:</P>
<P>
<PRE>CMyClass<int> myClass(15);
</PRE>
<P>The preceding line creates a CMyClass object that uses integers in place of the
abstract data type. If you wanted the class to deal with floating-point values, you'd
create an object of the class something like this:</P>
<P>
<PRE>CMyClass<float> myClass(15.75);
</PRE>
<P>For a more complete example, suppose you want to create a class that stores two
values and has member functions that compare those values. Listing 26.11 is a program
that does just that. First, the listing defines a class template called CCompare.
This class stores two values that are supplied to the constructor. The class also
includes the usual constructor and destructor, as well as member functions for determining
the larger or smaller of the values, or whether the values are equal.</P>
<P>
<H4>Listing 26.11  TEMPLATE3.CPP--Using a Class Template</H4>
<PRE>#include <iostream.h>
template<class Type>
class CCompare
{
protected:
Type arg1;
Type arg2;
public:
CCompare(Type arg1, Type arg2)
{
CCompare::arg1 = arg1;
CCompare::arg2 = arg2;
}
~CCompare() {}
Type GetMin()
{
Type min;
if (arg1 < arg2)
min = arg1;
else
min = arg2;
return min;
}
Type GetMax()
{
Type max;
if (arg1 > arg2)
max = arg1;
else
max = arg2;
return max;
}
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -