📄 ch20.htm
字号:
93: intArray[j] = j;94: cout << "intArray[" << j << "] okay..." << endl;95: }96: }97: catch (Array::xBoundary)98: {99: cout << "Unable to process your input!\n";100: }101: cout << "Done.\n";102: return 0;<TT>103: }</TT></FONT><FONT COLOR="#0066FF">Output: intArray[0] okay...intArray[1] okay...intArray[2] okay...intArray[3] okay...intArray[4] okay...intArray[5] okay...intArray[6] okay...intArray[7] okay...intArray[8] okay...intArray[9] okay...intArray[10] okay...intArray[11] okay...intArray[12] okay...intArray[13] okay...intArray[14] okay...intArray[15] okay...intArray[16] okay...intArray[17] okay...intArray[18] okay...intArray[19] okay...Unable to process your input!Done.</FONT></PRE><P><FONT COLOR="#000077"><B>Analysis:</B></FONT><B> </B>Listing 20.1 presents a somewhatstripped-down <TT>Array</TT> class, based on the template developed on Day 19, "Templates."On line 23, a new class is contained within the declaration of the boundary.<BR><BR>This new class is not in any way distinguished as an exception class. It is justa class like any other. This particular class is incredibly simple: It has no dataand no methods. Nonetheless, it is a valid class in every way.</P><P>In fact, it is incorrect to say it has no methods, because the compiler automaticallyassigns it a default constructor, destructor, copy constructor, and the copy operator(operator equals); so it actually has four class functions, but no data.</P><P>Note that declaring it from within <TT>Array</TT> serves only to couple the twoclasses together. As discussed on Day 15, "Advanced Inheritance," <TT>Array</TT>has no special access to <TT>xBoundary</TT>, nor does <TT>xBoundary</TT> have preferentialaccess to the members of <TT>Array</TT>.</P><P>On lines 60-66 and 69-75, the offset operators are modified to examine the offsetrequested and, if it is out of range, to throw the <TT>xBoundary</TT> class as anexception. The parentheses are required to distinguish between this call to the <TT>xBoundary</TT>constructor and the use of an enumerated constant. Note that Microsoft requires thatyou provide a <TT>return</TT> statement to match the declaration (in this case, returningan integer reference), even though if an exception is thrown on line 65 the codewill never reach line 66. This is a compiler bug, proving only that even Microsoftfinds this stuff difficult and confusing!</P><P>On line 89, the keyword <TT>try</TT> begins a <TT>try</TT> block that ends online 96. Within that <TT>try</TT> block, 100 integers are added to the array thatwas declared on line 88.</P><P>On line 97, the <TT>catch</TT> block to catch <TT>xBoundary</TT> exceptions isdeclared.</P><P>In the driver program on lines 86-103, a <TT>try</TT> block is created in whicheach member of the array is initialized. When <TT>j</TT> (line 91) is incrementedto 20, the member at offset 20 is accessed. This causes the test on line 63 to fail,and <TT>operator[]</TT> raises an <TT>xBoundary</TT> exception on line 65.</P><P>Program control switches to the <TT>catch</TT> block on line 97, and the exceptionis caught or handled by the <TT>catch</TT> on the same line, which prints an errormessage. Program flow drops through to the end of the <TT>catch</TT> block on line100.<H3 ALIGN="CENTER"><A NAME="Heading9"></A><FONT COLOR="#000077">try Blocks</FONT></H3><P>A <TT>try</TT> block is a set of statements that begins with the word <TT>try</TT>,is followed by an opening brace, and ends with a closing brace. Example:</P><PRE><FONT COLOR="#0066FF">try{Function();};</FONT></PRE><H3 ALIGN="CENTER"><A NAME="Heading10"></A><FONT COLOR="#000077">catch Blocks</FONT></H3><P>A <TT>catch</TT> block is a series of statements, each of which begins with theword <TT>catch</TT>, followed by an exception type in parentheses, followed by anopening brace, and ending with a closing brace. Example:</P><PRE><FONT COLOR="#0066FF">try{Function();};catch (OutOfMemory){// take action}</FONT></PRE><H3 ALIGN="CENTER"><A NAME="Heading11"></A><FONT COLOR="#000077">Using try Blocksand catch Blocks</FONT></H3><P>Figuring out where to put your <TT>try</TT> blocks is non-trivial: It is not alwaysobvious which actions might raise an exception. The next question is where to catchthe exception. It may be that you'll want to throw all memory exceptions where thememory is allocated, but you'll want to catch the exceptions high in the program,where you deal with the user interface.</P><P>When trying to determine <TT>try</TT> block locations, look to where you allocatememory or use resources. Other things to look for are out-of-bounds errors, illegalinput, and so forth.<H4 ALIGN="CENTER"><A NAME="Heading12"></A><FONT COLOR="#000077">Catching Exceptions</FONT></H4><P>Here's how it works: when an exception is thrown, the call stack is examined.The call stack is the list of function calls created when one part of the programinvokes another function.</P><P>The call stack tracks the execution path. If <TT>main()</TT> calls the function<TT>Animal::GetFavoriteFood()</TT>, and <TT>GetFavoriteFood()</TT> calls <TT>Animal::LookupPreferences()</TT>,which in turn calls <TT>fstream::operator>>()</TT>, all these are on the callstack. A recursive function might be on the call stack many times.</P><P>The exception is passed up the call stack to each enclosing block. As the stackis unwound, the destructors for local objects on the stack are invoked, and the objectsare destroyed.</P><P>After each <TT>try</TT> block there is one or more <TT>catch</TT> statements.If the exception matches one of the <TT>catch</TT> statements, it is considered tobe handled by having that statement execute. If it doesn't match any, the unwindingof the stack continues.</P><P>If the exception reaches all the way to the beginning of the program (<TT>main()</TT>)and is still not caught, a built-in handler is called that terminates the program.</P><P>It is important to note that the exception unwinding of the stack is a one-waystreet. As it progresses, the stack is unwound and objects on the stack are destroyed.There is no going back: Once the exception is handled, the program continues afterthe <TT>try</TT> block of the <TT>catch</TT> statement that handled the exception.</P><P>Thus, in Listing 20.1, execution will continue on line 101, the first line afterthe <TT>try</TT> block of the <TT>catch</TT> statement that handled the <TT>xBoundary</TT>exception. Remember that when an exception is raised, program flow continues afterthe <TT>catch</TT> block, not after the point where the exception was thrown.<H4 ALIGN="CENTER"><A NAME="Heading13"></A><FONT COLOR="#000077">More Than One catchSpecification</FONT></H4><P>It is possible for more than one condition to cause an exception. In this case,the <TT>catch</TT> statements can be lined up one after another, much like the conditionsin a <TT>switch</TT> statement. The equivalent to the <TT>default</TT> statementis the "catch everything" statement, indicated by <TT>catch(...)</TT>.Listing 20.2 illustrates multiple exception conditions.</P><P><A NAME="Heading14"></A><FONT SIZE="4" COLOR="#000077"><B>Listing 20.2. Multipleexceptions.</B></FONT><PRE><FONT COLOR="#0066FF">0: #include <iostream.h>1: 2: const int DefaultSize = 10;3: 4: class Array5: {6: public:7: // constructors8: Array(int itsSize = DefaultSize);9: Array(const Array &rhs);10: ~Array() { delete [] pType;}11: 12: // operators13: Array& operator=(const Array&);14: int& operator[](int offSet);15: const int& operator[](int offSet) const;16: 17: // accessors18: int GetitsSize() const { return itsSize; }19: 20: // friend function21: friend ostream& operator<< (ostream&, const Array&);22: 23: // define the exception classes24: class xBoundary {};25: class xTooBig {};26: class xTooSmall{};27: class xZero {};28: class xNegative {};29: private:30: int *pType;31: int itsSize;32: };33: 34: int& Array::operator[](int offSet)35: {36: int size = GetitsSize();37: if (offSet >= 0 && offSet < GetitsSize())38: return pType[offSet];39: throw xBoundary();40: return pType[0]; // appease MFC41: }42: 43: 44: const int& Array::operator[](int offSet) const45: {46: int mysize = GetitsSize();47: if (offSet >= 0 && offSet < GetitsSize())48: return pType[offSet];49: throw xBoundary();50: return pType[0];51: return pType[0]; // appease MFC52: }53: 54: 55: Array::Array(int size):56: itsSize(size)57: {58: if (size == 0)59: throw xZero();60: if (size < 10)61: throw xTooSmall();62: if (size > 30000)63: throw xTooBig();64: if (size < 1)65: throw xNegative();66: 67: pType = new int[size];68: for (int i = 0; i<size; i++)69: pType[i] = 0;70: }71: 72: 73: 74: int main()75: {76: 77: try78: {79: Array intArray(0);80: for (int j = 0; j< 100; j++)81: {82: intArray[j] = j;83: cout << "intArray[" << j << "] okay...\n";84: }85: }86: catch (Array::xBoundary)87: {88: cout << "Unable to process your input!\n";89: }90: catch (Array::xTooBig)91: {92: cout << "This array is too big...\n";93: }94: catch (Array::xTooSmall)95: {96: cout << "This array is too small...\n";97: }98: catch (Array::xZero)99: {100: cout << "You asked for an array";101: cout << " of zero objects!\n"; 102: }103: catch (...)104: {105: cout << "Something went wrong!\n";106: }107: cout << "Done.\n";108: return 0;<TT>109: }</TT></FONT><FONT COLOR="#0066FF">Output: You asked for an array of zero objects!Done.</FONT></PRE><DL> <DD><FONT COLOR="#0066FF"></FONT></DL><P><FONT COLOR="#000077"><B>Analysis:</B></FONT><B> </B>Four new classes are createdin lines 24-27: <TT>xTooBig</TT>,<TT> xTooSmall</TT>,<TT> xZero</TT>, and <TT>xNegative</TT>.In the constructor, on lines 55-70, the size passed to the constructor is examined.If it's too big, too small, negative, or zero, an exception is thrown.</P><P>The <TT>try</TT> block is changed to include <TT>catch</TT> statements for eachcondition other than negative, which is caught by the "catch everything"statement <TT>catch(...)</TT>, shown on line 103.</P><P>Try this with a number of values for the size of the array. Then try putting in<TT>-5</TT>. You might have expected <TT>xNegative</TT> to be called, but the orderof the tests in the constructor prevented this: <TT>size < 10</TT> was evaluatedbefore <TT>size < 1</TT>. To fix this, swap lines 60 and 61 with lines 64 and65 and recompile.<H4 ALIGN="CENTER"><A NAME="Heading15"></A><FONT COLOR="#000077">Exception Hierarchies</FONT></H4><P>Exceptions are classes, and as such they can be derived from. It may be advantageousto create a class <TT>xSize</TT>, and to derive from it <TT>xZero</TT>,<TT> xTooSmall</TT>,<TT>xTooBig</TT>, and <TT>xNegative</TT>. Thus, some functions might just catch <TT>xSize</TT>errors, while other functions might catch the specific type of <TT>xSize</TT> error.Listing 20.3 illustrates this idea.</P><P><A NAME="Heading16"></A><FONT SIZE="4" COLOR="#000077"><B>Listing 20.3. Classhierarchies and exceptions.</B></FONT><PRE><FONT COLOR="#0066FF">0: #include <iostream.h>1: 2: const int DefaultSize = 10;3: 4: class Array5: {6: public:7: // constructors8: Array(int itsSize = DefaultSize);9: Array(const Array &rhs);10: ~Array() { delete [] pType;}11: 12: // operators13: Array& operator=(const Array&);14: int& operator[](int offSet);15: const int& operator[](int offSet) const;16: 17: // accessors18: int GetitsSize() const { return itsSize; }19: 20: // friend function21: friend ostream& operator<< (ostream&, const Array&);22: 23: // define the exception classes24: class xBoundary {};25: class xSize {};26: class xTooBig : public xSize {};27: class xTooSmall : public xSize {};28: class xZero : public xTooSmall {};29: class xNegative : public xSize {};30: private:31: int *pType;32: int itsSize;33: };34: 35: 36: Array::Array(int size):37: itsSize(size)38: {39: if (size == 0)40: throw xZero();41: if (size > 30000)42: throw xTooBig();43: if (size <1)44: throw xNegative();45: if (size < 10)46: throw xTooSmall();47: 48: pType = new int[size];49: for (int i = 0; i<size; i++)50: pType[i] = 0;51: }52: 53: int& Array::operator[](int offSet)54: {55: int size = GetitsSize();56: if (offSet >= 0 && offSet < GetitsSize())57: return pType[offSet];58: throw xBoundary();59: return pType[0]; // appease MFC60: }61: 62: 63: const int& Array::operator[](int offSet) const64: {65: int mysize = GetitsSize();66: if (offSet >= 0 && offSet < GetitsSize())67: return pType[offSet];68: throw xBoundary();69: return pType[0];70: return pType[0]; // appease MFC71: }72: 73: int main()74: {75: 76: try77: {78: Array intArray(5);79: for (int j = 0; j< 100; j++)80: {81: intArray[j] = j;82: cout << "intArray[" << j << "] okay...\n";83: }84: }85: catch (Array::xBoundary)86: {87: cout << "Unable to process your input!\n";88: }89: catch (Array::xTooBig)90: {91: cout << "This array is too big...\n";92: }93: 94: catch (Array::xZero)95: {96: cout << "You asked for an array";
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -