Standard C++ Exception Handling Syntax

From RAD Studio
Jump to: navigation, search

Go Up to Standard C++ Exception Handling


Exception handling requires the use of three keywords: try, throw, and catch. The throw keyword is used to generate an exception. The try block contains statements that might throw exceptions and is followed by one or more catch statements. Each catch statement handles a specific type of exception.

Note The try, catch, and throw keywords are not allowed in C programs.

The try block

The try block contains a statement or statements that can throw an exception. A program throws an exception by executing a throw statement. The throw statement generally occurs within a function. For example:

void SetFieldValue(DF *dataField, int userValue)
{
    if ((userValue < 0) || userValue > 10)
        throw EIntegerRange(0, 10, userValue);
    // ...
}

Another part of the program can catch the thrown exception object and handle it accordingly. For example:

try {
	SetFieldValue(dataField, userValue);
}
catch (EIntegerRange &rangeErr) {
	printf("Expected value between %d and %d, but got %d\n", rangeErr.min,
		rangeErr.max, rangeErr.value);
}

In the previous example, if the function SetFieldValue finds that its input parameters are invalid, it can throw an exception to indicate this. If an exception is thrown, then the printf statement is executed. If no exception is thrown, then the printf statement is not executed.

A try block specified by try must be followed immediately by the handler specified by catch. The try block is a statement that specifies the flow of control as the program executes. If an exception is thrown in the try block, program control is transferred to the appropriate exception handler.

The handler is a block of code designed to handle the exception. The C++ language requires at least one handler immediately after a try block. The program should include a handler for each exception that the program can generate.

The throw statement

The throw statement can throw various types of objects. Objects in C++ may generally be thrown by value, reference, or pointer. For example:

// throw an object to be caught by value or reference
throw EIntegerRange(0, 10, userValue);

// throw an object to be caught by pointer
throw new EIntegerRange(0, 10, userValue);

Note: Throw exceptions by value and catch exceptions by reference to prevent memory leaks. If you catch an exception by pointer, you might not be able to delete the exception object.

Note: To throw an exception by value, it must have a public copy constructor and public destructor.

In addition, the throw statement can throw primitive types, such as integers or pointers. The next two examples show features that are provided largely for completeness in the standard. It is better to throw more descriptive exceptions. There are special cases for throwing built-in types, such as integers. Also, it is preferable not to throw exceptions by pointer.

// throw an integer
throw 1;

// throw a char *
throw "foo";

Using an exception object instead of a primitive value can be more efficient, because:

  • The exception can have a better description.
  • The exception can be filtered with the help of class polymorphism.

For most cases, you want to catch exceptions by reference, and especially by const reference. There are some cases where you want to be careful about catching objects by value. Objects that are caught by value must be copied before they are assigned to the catch parameter. If a user supplies a copy constructor, this is called, and this can add inefficiency.

The catch statement

The catch statement has several forms. Objects may be caught by value, reference, or pointer. In addition, const modifiers can be applied to the catch parameter. There can be multiple catch statements for a single try block to allow a block to catch multiple different kinds of exceptions, and there should be a catch statement for each exception that might be thrown. For example:

try
    CommitChange(dataBase, recordMods);
catch (const EIntegerRange &rangeErr)
    printf("Got an integer range exception");
catch (const EFileError &fileErr)
    printf("Got a file I/O error");

If the CommitChange function uses multiple subsystems, and these subsystems can throw different types of exceptions, you may want to handle each type of exception separately. With multiple catch statements for a single try statement, you can have handlers for each type of exception.

If an exception object is derived from some base class, you may want to add specialized handlers for some derived exceptions, but also include a generic handler for the base class. You do this by placing the catch statements in the order that you want them to be searched when an exception is thrown. For example, the following code handles EIntegerRange first and then ERange, from which EIntegerRange is derived.

try
    SetFieldValue(dataField, userValue);
catch (const EIntegerRange &rangeErr)
    printf("Got an integer range exception");
catch (const ERange &rangeErr)
    printf("Got a range exception");

Last, if you want your handler to catch all exceptions that might be thrown past the try block, use the catch(...) special form. This tells the exception handling system that the handler should be invoked for any exception. For example:

try
    SetFieldValue(dataField, userValue);
catch (...)
    printf("Got an exception of some kind");

See Also