- Table of Contents
- Copyright
- About the Authors
- About the Contributors
- Acknowledgments
- Tell Us What You Think!
- Introduction
- How to Use This Book
- What You Need to Use This Book
- What's New in Visual C++ 6.0
- Contacting the Main Author
- Part I: Introduction
- Chapter 1. The Visual C++ 6.0 Environment
- Part II: MFC Programming
- Chapter 2. MFC Class Library Overview
- Chapter 3. MFC Message Handling Mechanism
- Chapter 4. The Document View Architecture
- Chapter 5. Creating and Using Dialog Boxes
- Chapter 6. Working with Device Contexts and GDI Objects
- Chapter 7. Creating and Using Property Sheets
- Chapter 8. Working with the File System
- Chapter 9. Using Serialization with File and Archive Objects
- Part III: Internet Programming with MFC
- Chapter 10. MFC and the Internet Server API (ISAPI)
- Chapter 11. The WinInet API
- Chapter 12. MFC HTML Support
- Part IV: Advanced Programming Topics
- Chapter 13. Using the Standard C++ Library
- Chapter 14. Error Detection and Exception Handling Techniques
- Structured Exception Handling
- C++ Exception Handling
- MFC Error and Exception Handling
- Summary
- Chapter 15. Debugging and Profiling Strategies
- Chapter 16. Multithreading
- Chapter 17. Using Scripting and Other Tools to Automate the Visual C++ IDE
- Part V: Database Programming
- Chapter 18. Creating Custom AppWizards
- Chapter 19. Database Overview
- Chapter 20. ODBC Programming
- Chapter 21. MFC Database Classes
- Chapter 22. Using OLE DB
- Chapter 23. Programming with ADO
- Part VI: MFC Support for COM and ActiveX
- Chapter 24. Overview of COM and Active Technologies
- Chapter 25. Active Documents
- Chapter 26. Active Containers
- Chapter 27. Active Servers
- Chapter 28. ActiveX Controls
- Part VII: Using the Active Template Library
- Chapter 29. ATL Architecture
- Chapter 30. Creating COM Objects Using ATL
- Chapter 31. Creating ActiveX Controls Using ATL
- Chapter 32. Using ATL to Create MTS and COM+ Components
- Part VIII: Finishing Touches
- Chapter 33. Adding Windows Help
- Part IX: Appendix
C++ Exception Handling
C++ exception handling enables you to use an object-oriented approach to handling exception errors created by C++ classes. You should use C++ exception handling rather than structured exception handling. Remember that SEH was designed to work with C, not C++. Although you may use SEH for C++, C++ exception handling is a better alternative, resulting in more portable and flexible code.
Using C++ Exceptions
C++ exceptions deal with the relationship of an application and a class member. When the application calls the class member and an error occurs, the class member informs the application of the error. The class member has done what is known as throwing an exception. The application takes this exception and handles it in a block of code called the catch block or the exception handler. It is in this block of code that the error can be dealt with and the program allowed to recover from this error. Taking a few steps back, the code that originally called the class member had to be included in a try block. The try block makes it possible to return the exception errors to the catch block. It is a guarded body of code that signifies what code is to return exception errors if they occur. Remem ber that code that does not interact with the class does not have to be in a try block.
To see whether C++ exception handling is enabled in your project, open the Project Settings dialog box. Select the C/C++ tab. Set the category to C++ Language. Check the Enable Exception Handling check box. You also may use the /GX compiler option.
The C++ Exception Structure
The skeletal structure for C++ exception handling follows:
class AnyClass // a class used for this example
{
public:
class AnyError // exception class
{
}
void AnyClassFunction() // member function
{
// Check for error.
// If there is one, then
throw(AnyError);
}
};
void main() //application
{
try // try block
{
AnyClass Object;
Object.AnyClassFunction();
}
catch (AnyClass::AnyError)
{
// perform processing on the error
}
}
If an error occurs in AnyClass, the AnyClassFunction throws an exception. When an exception occurs, the keyword throw is used, followed by the constructor class:
throw(AnyError);
Notice in the main() part of the program that any interaction with the class is guarded by the try statement:
try // try block
{
AnyClass Object;
Object.AnyClassFunction();
}
If an error is detected, an error is thrown and captured in the catch block:
catch (AnyClass::AnyError)
{
// perform processing on the error
}
An Exception Example
Code for a complete working example of handling an exception follows, using try, catch, and throw keywords. The exception that will be handled is the classic divide-by-zero error that has surely crashed everyone's program at one time or another.
The program consists of two parts: the class and application. Take a look at this example:
//Divide by Zero example
//Demonstration of exception handling
#include <iostream.h>
class DivNumbers
{
public:
class DivError //Exception class
{
};
DivNumbers() //Class constructor
{
}
int PerformDivide(int number1, int number2)
{
if (number2 == 0) //Check if divisor is going to be zero
throw DivError(); //If true, throw the exception error
return number1 / number2;
}
};
void main()
{
DivNumbers MyDivide;
int result = 0;
try
{
result = MyDivide.PerformDivide(10,5);
cout << result << endl;
result = MyDivide.PerformDivide(10,10);
cout << result << endl;
result = MyDivide.PerformDivide(10,0); //Error causing code
cout << result << endl;
}
catch(DivNumbers::DivError)
{
cout << "Divide by Zero Error encountered!" << endl;
}
cout << "End of Dividing Numbers." << endl;
}
The output follows:
2 1 Divide by Zero Error encountered! End of Dividing Numbers.
Let's take a look at exactly what the code is doing.
First, the class DivNumbers is declared. The public class exception DivError is created:
class DivError //Exception class
{
};
This class exception is empty, and that is okay. We just need the capability to throw the error at this point, so it just has to be declared. Code may be entered in this routine to gain more information about the error that has occurred.
Next, the throw command is inserted into the routine that performs the function that needs monitoring. In this case, it is the member function int PerformDivide.
int PerformDivide(int number1, int number2)
{
if (number2 == 0) //Check if divisor is going to be zero
throw DivError(); //If true, throw the exception error
return number1 / number2;
}
The logic in this function is simple to follow. The variable number2 will cause all the trouble, so a check is performed to see whether it equals zero. If the function ever receives a zero to divide with, the exception error is thrown with this command:
throw DivError();
The empty exception class DivError is thrown. Now our catch routine will catch this exception. Once caught, the code in our catch block will be executed:
catch(DivNumbers::DivError)
{
cout << "Divide by Zero Error encountered!" << endl;
}
The program will print out this message, and processing will continue on, down to the last line of code.
cout << "End of Dividing Numbers." << endl;
The control of the program continues on to the bottom of the application body without crashing the program. The exception handler also may redirect the program flow to different parts of the program if there is no way to recover from the error.
Multiple Exception Handling
A class can have many exceptions—not just one, as the preceding example shows.
Divide by zero is a nice and simple exception error that every programmer can relate to, so we'll stick with this as well as an unsigned integer check (when an unsigned integer operation results in a negative value). The code will be modified just a bit to allow for different errors to be detected and captured.
//Multiple Exception example
//Demonstration of multiple exception handling
#include <iostream.h>
class NumberProcesses
{
public:
class DivError //Exception class
{
};
class DivFloatError //Exception class
{
};
class UnsignedIntegerError //Exception class
{
};
NumberProcesses() //Class constructor
{
}
int PerformDivide(int number1, int number2)
{
if (number2 == 0) //Check if divisor is going to be zero
throw DivError(); //If true, throw the exception error
return number1 / number2;
}
float PerformFloatDivide(float number1, float number2)
{
if (number2 == 0.0) //Check if divisor is going to be zero
throw DivFloatError(); //If true, throw the exception error
return number1 / number2;
}
unsigned int PerformUnsignedSubtraction(int number1, int number2)
{
if (number1 - number2 < 0) //Check if divisor is going to be zero
throw UnsignedIntegerError(); //If true, throw the exception error
return number1 - number2;
}
};
void main()
{
NumberProcesses MyNumbers;
int result = 0;
int unsigned_result = 0;
float float_result = 0.0;
//try block code for Integer operations
try
{
cout << "TEST: Integer Divide by Zero" << endl;
result = MyNumbers.PerformDivide(10,5);
cout << result << endl;
result = MyNumbers.PerformDivide(10,10);
cout << result << endl;
result = MyNumbers.PerformDivide(10,0); //Error causing code
cout << result << endl;
}
//catch for Integer must immediately follow try block
catch(NumberProcesses::DivError)
{
cout << "Integer Divide by Zero Error encountered!" << endl << endl;
}
//try block code for Float operations
try
{
cout << "TEST: Float Divide by Zero" << endl;
float_result = MyNumbers.PerformFloatDivide(10.0,5.5);
cout << float_result << endl;
float_result = MyNumbers.PerformFloatDivide(10.0,9.5);
cout << float_result << endl;
float_result = MyNumbers.PerformFloatDivide(10.0,0.0); //Error causing code
cout << float_result << endl;
}
//catch for Float must immediately follow try block
catch(NumberProcesses::DivFloatError)
{
cout << "Float Divide by Zero Error encountered!" << endl << endl;
}
//try block code for Unsigned Integer operations
try
{
cout << "TEST: Unsigned Integer" << endl;
unsigned_result = MyNumbers.PerformUnsignedSubtraction(5,4);
cout << unsigned_result << endl;
unsigned_result = MyNumbers.PerformUnsignedSubtraction(5,5);
cout << unsigned_result << endl;
unsigned_result = MyNumbers.PerformUnsignedSubtraction(5,6);
cout << unsigned_result << endl;
}
//catch for Unsigned Integer must immediately follow try block
catch(NumberProcesses::UnsignedIntegerError)
{
cout << "Unsigned Integer Error encountered!" << endl << endl;
}
cout << "End of Processing Numbers." << endl;
}
The output of this program follows:
TEST: Integer Divide By Zero 2 1 Integer Divide By Zero Error Encountered! Test: Float Divide By Zero 1.81818 1.05263 Float Divide By Zero Error Encountered! Test: Unsigned Integer 1 0 Unsigned Integer Error Encountered! End of Processing Numbers.
Note that the constructor used for each try block must immediately follow the corresponding try block. This example performs the same test as the preceding example—dividing integers by zero. Then a similar test is performed for floating-point numbers. Finally, the exception error for checking whether an unsigned integer function results in a negative number occurs. Three separate exception classes are created and called by three separate throw statements.
Using Arguments with Exceptions
At times, you may need the exception error to return specific information on what caused the exception error, such as which function called the class member to cause the exception. Other such information may involve displaying the values passed to the class member, or possibly a combination of both. This section covers a method to pass such information to make the exception messages more flexible and informative. You can accomplish this by using arguments with exceptions.
The following program example focuses on the unsigned integer exception. Now whenever an exception is thrown, the function name that caused the exception to be thrown is indicated.
//Multiple Exception example
//Demonstration of multiple exception handling
#include <iostream.h>
#include <string.h>
class NumberProcesses
{
public:
class UnsignedIntegerError //Exception class
{
public:
char calling_function[80]; //String to hold function name
int result_value1; //First number used for subtraction
int result_value2; //Second number used for subtraction
UnsignedIntegerError(char* function_name, int number1, int number2)
{
strcpy(calling_function, function_name); //Assignments
result_value1 = number1;
result_value2 = number2;
};
NumberProcesses() //Class constructor
{
}
unsigned int PerformUnsignedSubtraction(int number1, int number2)
{
if (number1 - number2 < 0) //Check if divisor is going to be zero
throw UnsignedIntegerError("PerformUnsignedSubtraction", number1, number2);
//If true, throw the exception error
return number1 - number2;
}
};
void main()
{
NumberProcesses MyNumbers;
int unsigned_result = 0;
//try block code for Unsigned Integer operations
try
{
cout << "TEST: Unsigned Integer" << endl;
unsigned_result = MyNumbers.PerformUnsignedSubtraction(5,4);
cout << unsigned_result << endl;
unsigned_result = MyNumbers.PerformUnsignedSubtraction(5,5);
cout << unsigned_result << endl;
unsigned_result = MyNumbers.PerformUnsignedSubtraction(5,6);
cout << unsigned_result << endl;
}
//catch for Unsigned Integer must immediately follow try block
catch(NumberProcesses::UnsignedIntegerError ExceptionObject) // Declare object ExceptionObject
{
cout << endl << "Unsigned Integer Error encountered!" << endl << endl;
cout << "Function: " << ExceptionObject.calling_function << endl;
cout << "Trying to subtract the following numbers:" << endl;
cout << ExceptionObject.result_value1 << " - "<< ExceptionObject.result_value2 << endl;
cout << "Result must always be positive for this function." << endl << endl;
}
cout << "End of Processing Numbers." << endl;
}
The output of this program looks like this:
TEST: Unsigned Integer 1 0 Unsigned Integer Error Occurred! Function: PerformUnsignedSubtraction Trying to subtract the following numbers: 5 - 6 Result must always be positive for this function. End of Processing Numbers.
Let's take a look at how the exception class looks for UnsignedIntegerError:
class UnsignedIntegerError //Exception class
{
public:
char calling_function[80]; //String to hold function name
int result_value1; //First number used for subtraction
int result_value2; //Second number used for subtraction
UnsignedIntegerError(char* function_name, int number1, int number2)
{
strcpy(calling_function, function_name); //Assignments
result_value1 = number1;
result_value2 = number2;
};
};
Essentially the exception class now has public variables defined. These variables will hold the data you want to report to the user. Declared here is a string variable to hold the name of the function that caused the error. The other two variables will hold the values being sent to the operation.
The throw statement now looks like this:
throw UnsignedIntegerError("PerformUnsignedSubtraction", number1, number2);
It has been expanded to pass all three values to the exception class. Note that the first parameter is the name of the function with the throw statement. (Of course, this string can hold any value you want, but it pays to be informative.)
The final step is to obtain the information on what the exception has caught. The catch statement looks like this:
catch(NumberProcesses::UnsignedIntegerError ExceptionObject) // Declare object ExceptionObject
{
cout << endl << "Unsigned Integer Error encountered!" << endl << endl;
cout << "Function: " << ExceptionObject.calling_function << endl;
cout << "Trying to subtract the following numbers:" << endl;
cout << ExceptionObject.result_value1 << " - "<< ExceptionObject.result_value2 << endl;
cout << "Result must always be positive for this function." << endl << endl;
}
This statement has evolved into something a little more involved. The first statement declares an exception object (appropriately titled ExceptionObject):
catch(NumberProcesses::UnsignedIntegerError ExceptionObject) // Declare object
This makes it simple to reference the values that reside in the exception. Just remember that this method is totally customizable, and using arguments with exceptions is a very powerful tool.
Exception-Handling Overhead
There is cost of performance and size using the C++ exception handling mechanism. It may increase the size of the resulting executable and slow down the run speed of the program. This is one of the reasons why exceptions should be used only to signal unusual or show-stopping program events. They should be used only where necessary. Sometimes displaying an error message to the user may be enough, instead of throwing an exception.
The /GX compiler option enables C++ exception handling. You can turn off exception handling by using the /GX- option.
MFC Error and Exception Handling | Next Section

Account Sign In
View your cart