Exception Handling in MFC

Delete_exception

This is defined at STDAFX.h

Code:
#define DELETE_EXCEPTION(e) do { e->Delete(); } while (0)

Exception Handling in MFC

In this article

  1. When to Use Exceptions
  2. MFC Exception Support
  3. Further Reading About Exceptions
  4. See also

This article explains the exception-handling mechanisms available in MFC. Two mechanisms are available:

  • C++ exceptions, available in MFC version 3.0 and later

  • The MFC exception macros, available in MFC versions 1.0 and later

If you're writing a new application using MFC, you should use the C++ mechanism. You can use the macro-based mechanism if your existing application already uses that mechanism extensively.

You can readily convert existing code to use C++ exceptions instead of the MFC exception macros. Advantages of converting your code and guidelines for doing so are described in the article Exceptions: Converting from MFC Exception Macros.

If you have already developed an application using the MFC exception macros, you can continue using these macros in your existing code, while using C++ exceptions in your new code. The article Exceptions: Changes to Exception Macros in Version 3.0 gives guidelines for doing so.

 Note

To enable C++ exception handling in your code, select Enable C++ Exceptions on the Code Generation page in the C/C++ folder of the project's Property Pages dialog box, or use the /EHsc compiler option.

This article covers the following topics:

When to Use Exceptions

Three categories of outcomes can occur when a function is called during program execution: normal execution, erroneous execution, or abnormal execution. Each category is described below.

  • Normal execution

    The function may execute normally and return. Some functions return a result code to the caller, which indicates the outcome of the function. The possible result codes are strictly defined for the function and represent the range of possible outcomes of the function. The result code can indicate success or failure or can even indicate a particular type of failure that is within the normal range of expectations. For example, a file-status function can return a code that indicates that the file does not exist. Note that the term "error code" is not used because a result code represents one of many expected outcomes.

  • Erroneous execution

    The caller makes some mistake in passing arguments to the function or calls the function in an inappropriate context. This situation causes an error, and it should be detected by an assertion during program development. (For more information on assertions, see C/C++ Assertions.)

  • Abnormal execution

    Abnormal execution includes situations where conditions outside the program's control, such as low memory or I/O errors, are influencing the outcome of the function. Abnormal situations should be handled by catching and throwing exceptions.

Using exceptions is especially appropriate for abnormal execution.

MFC Exception Support

Whether you use the C++ exceptions directly or use the MFC exception macros, you will use CException Class or CException-derived objects that may be thrown by the framework or by your application.

The following table shows the predefined exceptions provided by MFC.

Exception classMeaning
CMemoryException Class Out-of-memory
CFileException Class File exception
CArchiveException Class Archive/Serialization exception
CNotSupportedException Class Response to request for unsupported service
CResourceException Class Windows resource allocation exception
CDaoException Class Database exceptions (DAO classes)
CDBException Class Database exceptions (ODBC classes)
COleException Class OLE exceptions
COleDispatchException Class Dispatch (automation) exceptions
CUserException Class Exception that alerts the user with a message box, then throws a generic CException Class

Since version 3.0, MFC has used C++ exceptions but still supports its older exception handling macros, which are similar to C++ exceptions in form. Although these macros are not recommended for new programming, they are still supported for backward compatibility. In programs that already use the macros, you can freely use C++ exceptions as well. During preprocessing, the macros evaluate to the exception handling keywords defined in the MSVC implementation of the C++ language as of Visual C++ version 2.0. You can leave existing exception macros in place while you begin to use C++ exceptions. For information on mixing macros and C++ exception handling and on converting old code to use the new mechanism, see the articles Exceptions: Using MFC Macros and C++ Exceptions and Exceptions: Converting from MFC Exception Macros. The older MFC exception macros, if you still use them, evaluate to C++ exception keywords. See Exceptions: Changes to Exception Macros in Version 3.0. MFC does not directly support Windows NT structured exception handlers (SEH), as discussed in Structured Exception Handling.

Further Reading About Exceptions

The following articles explain using the MFC library for exception handing:

The following articles compare the MFC exception macros with the C++ exception keywords and explain how you can adapt your code:

See also

Modern C++ best practices for exceptions and error handling

Exceptions: Changes to Exception Macros in Version 3.0

 

In this article

  1. Exception Types and the CATCH Macro
  2. Re-Throwing Exceptions
  3. See also

This is an advanced topic.

In MFC version 3.0 and later, the exception-handling macros have been changed to use C++ exceptions. This article tells how those changes can affect the behavior of existing code that uses the macros.

This article covers the following topics:

Exception Types and the CATCH Macro

In earlier versions of MFC, the CATCH macro used MFC run-time type information to determine an exception's type; the exception's type is determined, in other words, at the catch site. With C++ exceptions, however, the exception's type is always determined at the throw site by the type of the exception object that is thrown. This will cause incompatibilities in the rare case where the type of the pointer to the thrown object differs from the type of the thrown object.

The following example illustrates the consequence of this difference between MFC version 3.0 and earlier versions:

C++
TRY
{
   THROW((CException*) new CCustomException());
}
CATCH(CCustomException, e)
{
   TRACE("MFC 2.x will land here\n");
}
AND_CATCH(CException, e)
{
   TRACE("MFC 3.0 will land here\n");
}
END_CATCH

This code behaves differently in version 3.0 because control always passes to the first catch block with a matching exception-declaration. The result of the throw expression

C++
THROW((CException*) new CCustomException());

is thrown as a CException*, even though it is constructed as a CCustomException. The CATCH macro in MFC versions 2.5 and earlier uses CObject::IsKindOf to test the type at run time. Because the expression

C++
e->IsKindOf(RUNTIME_CLASS(CException));

is true, the first catch block catches the exception. In version 3.0, which uses C++ exceptions to implement many of the exception-handling macros, the second catch block matches the thrown CException.

Code like this is uncommon. It usually appears when an exception object is passed to another function that accepts a generic CException*, performs "pre-throw" processing, and finally throws the exception.

To work around this problem, move the throw expression from the function to the calling code and throw an exception of the actual type known to the compiler at the time the exception is generated.

Re-Throwing Exceptions

A catch block cannot throw the same exception pointer that it caught.

For example, this code was valid in previous versions, but will have unexpected results with version 3.0:

C++
TRY
{
   // Do something to throw an exception.
   AfxThrowUserException();
}
CATCH(CException, e)
{
   THROW(e);    // Wrong. Use THROW_LAST() instead
}
END_CATCH
   }

Using THROW in the catch block causes the pointer e to be deleted, so that the outer catch site will receive an invalid pointer. Use THROW_LAST to re-throw e.

For more information, see Exceptions: Catching and Deleting Exceptions.

Exceptions: Catching and Deleting Exceptions

 

The following instructions and examples show you how to catch and delete exceptions. For more information on the trycatch, and throw keywords, see Modern C++ best practices for exceptions and error handling.

Your exception handlers must delete exception objects they handle, because failure to delete the exception causes a memory leak whenever that code catches an exception.

Your catch block must delete an exception when:

  • The catch block throws a new exception.

    Of course, you must not delete the exception if you throw the same exception again:

    C++
    catch (CException* e)
    {
       if (m_bThrowExceptionAgain)
          throw; // Do not delete e
       else
          e->Delete();
    }
    
  • Execution returns from within the catch block.

 Note

When deleting a CException, use the Delete member function to delete the exception. Do not use the delete keyword, because it can fail if the exception is not on the heap.

To catch and delete exceptions

  1. Use the try keyword to set up a try block. Execute any program statements that might throw an exception within a try block.

    Use the catch keyword to set up a catch block. Place exception-handling code in a catch block. The code in the catch block is executed only if the code within the try block throws an exception of the type specified in the catch statement.

    The following skeleton shows how try and catch blocks are normally arranged:

    C++
    try
    {
       // Execute some code that might throw an exception.
       AfxThrowUserException();
    }
    catch (CException* e)
    {
       // Handle the exception here.
       // "e" contains information about the exception.
       e->Delete();
    }
    

    When an exception is thrown, control passes to the first catch block whose exception-declaration matches the type of the exception. You can selectively handle different types of exceptions with sequential catch blocks as listed below:

    C++
    try
    {
       // Execute some code that might throw an exception.
       AfxThrowUserException();
    }
    catch (CMemoryException* e)
    {
       // Handle the out-of-memory exception here.
       e->Delete();
    }
    catch (CFileException* e)
    {
       // Handle the file exceptions here.
       e->Delete();
    }
    catch (CException* e)
    {
       // Handle all other types of exceptions here.
       e->Delete();
    }
    

For more information, see Exceptions: Converting from MFC Exception Macros.

Exceptions: Converting from MFC Exception Macros

 

In this article

  1. Advantages of Converting
  2. Doing the Conversion
  3. See also

This is an advanced topic.

This article explains how to convert existing code written with Microsoft Foundation Class macros — TRY, CATCH, THROW, and so on — to use the C++ exception-handling keywords trycatch, and throw. Topics include:

Advantages of Converting

You probably do not need to convert existing code, although you should be aware of differences between the macro implementations in MFC version 3.0 and the implementations in earlier versions. These differences and subsequent changes in code behavior are discussed in Exceptions: Changes to Exception Macros in Version 3.0.

The principal advantages of converting are:

  • Code that uses the C++ exception-handling keywords compiles to a slightly smaller .EXE or .DLL.

  • The C++ exception-handling keywords are more versatile: They can handle exceptions of any data type that can be copied (intfloatchar, and so on), whereas the macros handle exceptions only of class CException and classes derived from it.

The major difference between the macros and the keywords is that code using the macros "automatically" deletes a caught exception when the exception goes out of scope. Code using the keywords does not, so you must explicitly delete a caught exception. For more information, see the article Exceptions: Catching and Deleting Exceptions.

Another difference is syntax. The syntax for macros and keywords differs in three respects:

  1. Macro arguments and exception declarations:

    A CATCH macro invocation has the following syntax:

    CATCH( exception_classexception_object_pointer_name )

    Notice the comma between the class name and the object pointer name.

    The exception declaration for the catch keyword uses this syntax:

    catch( exception_type exception_name )

    This exception declaration statement indicates the type of exception the catch block handles.

  2. Delimitation of catch blocks:

    With the macros, the CATCH macro (with its arguments) begins the first catch block; the AND_CATCH macro begins subsequent catch blocks, and the END_CATCH macro terminates the sequence of catch blocks.

    With the keywords, the catch keyword (with its exception declaration) begins each catch block. There is no counterpart to the END_CATCH macro; the catch block ends with its closing brace.

  3. The throw expression:

    The macros use THROW_LAST to re-throw the current exception. The throw keyword, with no argument, has the same effect.

Doing the Conversion

To convert code using macros to use the C++ exception-handling keywords

  1. Locate all occurrences of the MFC macros TRY, CATCH, AND_CATCH, END_CATCH, THROW, and THROW_LAST.

  2. Replace or delete all occurrences of the following macros:

    TRY (Replace it with try)

    CATCH (Replace it with catch)

    AND_CATCH (Replace it with catch)

    END_CATCH (Delete it)

    THROW (Replace it with throw)

    THROW_LAST (Replace it with throw)

  3. Modify the macro arguments so that they form valid exception declarations.

    For example, change

    C++
    CATCH(CException, e)
    

    to

    C++
    catch (CException* e)
    
  4. Modify the code in the catch blocks so that it deletes exception objects as necessary. For more information, see the article Exceptions: Catching and Deleting Exceptions.

Here is an example of exception-handling code using MFC exception macros. Note that because the code in the following example uses the macros, the exception e is deleted automatically:

C++
TRY
{
   // Do something to throw an exception.
   AfxThrowUserException();
}
CATCH(CException, e)
{
   if (m_bPassExceptionsUp)
      THROW_LAST();

   if (m_bReturnFromThisFunction)
      return;

   // Not necessary to delete the exception e.
}
END_CATCH

The code in the next example uses the C++ exception keywords, so the exception must be explicitly deleted:

C++
try
{
   // Do something to throw an exception.
   AfxThrowUserException();
}
catch (CException* e)
{
   if (m_bPassExceptionsUp)
      throw;

   if (m_bThrowDifferentException)
   {
      e->Delete();
      throw new CMyOtherException;
   }

   if (m_bReturnFromThisFunction)
   {
      e->Delete();
      return;
   }

   e->Delete();
}

For more information, see Exceptions: Using MFC Macros and C++ Exceptions.

Exceptions: Using MFC Macros and C++ Exceptions

 

In this article

  1. Mixing Exception Keywords and Macros
  2. Try Blocks Inside Catch Blocks
  3. See also

This article discusses considerations for writing code that uses both the MFC exception-handling macros and the C++ exception-handling keywords.

This article covers the following topics:

Mixing Exception Keywords and Macros

You can mix MFC exception macros and C++ exception keywords in the same program. But you cannot mix MFC macros with C++ exception keywords in the same block because the macros delete exception objects automatically when they go out of scope, whereas code using the exception-handling keywords does not. For more information, see the article Exceptions: Catching and Deleting Exceptions.

The main difference between the macros and the keywords is that the macros "automatically" delete a caught exception when the exception goes out of scope. Code using the keywords does not; exceptions caught in a catch block must be explicitly deleted. Mixing macros and C++ exception keywords can cause memory leaks when an exception object is not deleted, or heap corruption when an exception is deleted twice.

The following code, for example, invalidates the exception pointer:

C++
TRY
{
   TRY
   {
   // Do something to throw an exception.
   AfxThrowUserException();
}
CATCH(CException, e)  // The "inner" catch block
{
   throw;  // Invalid attempt to throw exception
         // to the outer catch block below.
}
END_CATCH
}
CATCH(CException, e)  // The "outer" catch block
{
   // Pointer e is invalid because
   // it was deleted in the inner catch block.
}
END_CATCH

The problem occurs because e is deleted when execution passes out of the "inner" CATCH block. Using the THROW_LAST macro instead of the THROW statement will cause the "outer" CATCH block to receive a valid pointer:

C++
TRY
{
   TRY
   {
   // Do something to throw an exception.
   AfxThrowUserException();
}
CATCH(CException, e)  // The "inner" catch block
{
   THROW_LAST(); // Throw exception to the outer catch block below.
}
END_CATCH
}
CATCH(CException, e)  // The "outer" catch block
{
   // Pointer e is valid because
   // THROW_LAST() was used.
}
END_CATCH

Try Blocks Inside Catch Blocks

You cannot re-throw the current exception from within a try block that is inside a CATCH block. The following example is invalid:

C++
TRY
{
   // Do something to throw an exception.
   AfxThrowUserException();
}
CATCH(CException, e)
{
   try
   {
      throw;  // Wrong.  Causes e (the exception 
            // being thrown) to be deleted.
   }
   catch (CException* exception)
   {
      exception->ReportError();
   }
}
END_CATCH

For more information, see Exceptions: Examining Exception Contents.

Exceptions: Throwing Exceptions from Your Own Functions

 

It is possible to use the MFC exception-handling paradigm solely to catch exceptions thrown by functions in MFC or other libraries. In addition to catching exceptions thrown by library code, you can throw exceptions from your own code if you are writing functions that can encounter exceptional conditions.

When an exception is thrown, execution of the current function is stopped and jumps directly to the catch block of the innermost exception frame. The exception mechanism bypasses the normal exit path from a function. Therefore, you must be sure to delete those memory blocks that would be deleted in a normal exit.

To throw an exception

  1. Use one of the MFC helper functions, such as AfxThrowMemoryException. These functions throw a preallocated exception object of the appropriate type.

    In the following example, a function tries to allocate two memory blocks and throws an exception if either allocation fails:

    C++
    {
    char* p1 = (char*)malloc(SIZE_FIRST);
    if (p1 == NULL)
    AfxThrowMemoryException();
    char* p2 = (char*)malloc(SIZE_SECOND);
    if (p2 == NULL)
    {
       free(p1);
       AfxThrowMemoryException();
    }
    
    // ... Do something with allocated blocks ...
    
    // In normal exit, both blocks are deleted.
    free(p1);
    free(p2);
    }
    

    If the first allocation fails, you can simply throw the memory exception. If the first allocation is successful but the second one fails, you must free the first allocation block before throwing the exception. If both allocations succeed, you can proceed normally and free the blocks when exiting the function.

    • or -
  2. Use a user-defined exception to indicate a problem condition. You can throw an item of any type, even an entire class, as your exception.

    The following example attempts to play a sound through a wave device and throws an exception if there is a failure.

    C++
    #define WAVE_ERROR -5
       {
       // This Win32 API returns 0 if the sound cannot be played.
       // Throw an integer constant if it fails.
       if (!PlaySound(_T("SIREN.WAV"), NULL, SND_ASYNC))
          throw WAVE_ERROR;
       }
    

 Note

MFC's default handling of exceptions applies only to pointers to CException objects (and objects of CException-derived classes). The example above bypasses MFC's exception mechanism.

Exceptions: Database Exceptions

 

In this article

  1. Approaches to Exception Handling
  2. A Database Exception-Handling Example
  3. See also

This article explains how to handle database exceptions. Most of the material in this article applies whether you are working with the MFC classes for Open Database Connectivity (ODBC) or the MFC classes for Data Access Objects (DAO). Material specific to one or the other model is explicitly marked. Topics include:

Approaches to Exception Handling

The approach is the same whether you are working with DAO (obsolete) or ODBC.

You should always write exception handlers to handle exceptional conditions.

The most pragmatic approach to catching database exceptions is to test your application with exception scenarios. Determine the likely exceptions that might occur for an operation in your code, and force the exception to occur. Then examine the trace output to see what exception is thrown, or examine the returned error information in the debugger. This lets you know which return codes you'll see for the exception scenarios you are using.

Error Codes Used for ODBC Exceptions

In addition to return codes defined by the framework, which have names of the form AFX_SQL_ERROR_XXX, some CDBExceptions are based on ODBC return codes. The return codes for such exceptions have names of the form SQL_ERROR_XXX.

The return codes — both framework-defined and ODBC-defined — that the database classes can return are documented under the m_nRetCode data member of class CDBException. Additional information about return codes defined by ODBC is available in the ODBC Programmer's Reference.

Error Codes Used for DAO Exceptions

For DAO exceptions, more information is typically available. You can access error information through three data members of a caught CDaoException object:

  • m_pErrorInfo contains a pointer to a CDaoErrorInfo object that encapsulates error information in DAO's collection of error objects associated with the database.

  • m_nAfxDaoError contains an extended error code from the MFC DAO classes. These error codes, which have names of the form AFX_DAO_ERROR_XXX, are documented under the data member in CDaoException.

  • m_scode contains an OLE SCODE from DAO, if applicable. You'll seldom need to work with this error code, however. Usually more information is available in the other two data members. See the data member for more about SCODE values.

Additional information about DAO errors, the DAO Error object type, and the DAO Errors collection is available under class CDaoException.

A Database Exception-Handling Example

The following example attempts to construct a CRecordset-derived object on the heap with the new operator, and then open the recordset (for an ODBC data source). For a similar example for the DAO classes, see "DAO Exception Example" below.

ODBC Exception Example

The Open member function could throw an exception (of type CDBException for the ODBC classes), so this code brackets the Open call with a try block. The subsequent catch block will catch a CDBException. You could examine the exception object itself, called e, but in this case it is enough to know that the attempt to create a recordset has failed. The catch block displays a message box and cleans up by deleting the recordset object.

C++
CRecordset* CMyDatabaseDoc::GetRecordset()
{
   CCourses* pSet = new CCourses(&m_dbCust);
   try
   {
      pSet->Open();
   }
   catch (CDBException* e)
   {
      AfxMessageBox(e->m_strError, MB_ICONEXCLAMATION);
      // Delete the incomplete recordset object
      delete pSet;
      pSet = NULL;
      e->Delete();
   }
   return pSet;
}

DAO Exception Example

The DAO example is similar to the example for ODBC, but you can typically retrieve more kinds of information. The following code also attempts to open a recordset. If that attempt throws an exception, you can examine a data member of the exception object for error information. As with the previous ODBC example, it is probably enough to know that the attempt to create a recordset failed.

C++
CDaoRecordset* CMyDaoDatabaseDoc::GetRecordset()
{
   CDaoRecordset* pSet = new CCustSet(&m_db);
   try
   {
      pSet->Open();
   }
   catch (CDaoException* pe)
   {
      AfxMessageBox(pe->m_pErrorInfo->m_strDescription, MB_ICONEXCLAMATION);
      // Delete the incomplete recordset object
      delete pSet;
      pSet = NULL;
      pe->Delete();
   }
   return pSet;
}

This code gets an error message string from the m_pErrorInfo member of the exception object. MFC fills this member when it throws the exception.

For a discussion of the error information returned by a CDaoException object, see classes CDaoException and CDaoErrorInfo.

When you are working with Microsoft Jet (.mdb) databases, and in most cases when you are working with ODBC, there will be only one error object. In the rare case when you are using an ODBC data source and there are multiple errors, you can loop through DAO's Errors collection based on the number of errors returned by CDaoException::GetErrorCount. Each time through the loop, call CDaoException::GetErrorInfo to refill the m_pErrorInfo data member.

See also

Exception handling in MSVC

 

In this article

  1. Kinds of exceptions
  2. In this section
  3. See also

An exception is an error condition, possibly outside the program's control, that prevents the program from continuing along its regular execution path. Certain operations, including object creation, file input/output, and function calls made from other modules, are all potential sources of exceptions, even when your program is running correctly. Robust code anticipates and handles exceptions. To detect logic errors, use assertions rather than exceptions (see Using Assertions).

Kinds of exceptions

The Microsoft C++ compiler (MSVC) supports three kinds of exception handling:

  • C++ exception handling

    For most C++ programs, you should use C++ exception handling. It's type-safe, and ensures that object destructors are invoked during stack unwinding.

  • Structured exception handling

    Windows supplies its own exception mechanism, called structured exception handling (SEH). It's not recommended for C++ or MFC programming. Use SEH only in non-MFC C programs.

  • MFC exceptions

    Since version 3.0, MFC has used C++ exceptions. It still supports its older exception handling macros, which are similar to C++ exceptions in form. For advice about mixing MFC macros and C++ exceptions, see Exceptions: Using MFC Macros and C++ Exceptions.

Use an /EH compiler option to specify the exception handling model to use in a C++ project. Standard C++ exception handling (/EHsc) is the default in new C++ projects in Visual Studio.

We don't recommend you mix the exception handling mechanisms. For example, don't use C++ exceptions with structured exception handling. Using C++ exception handling exclusively makes your code more portable, and it allows you to handle exceptions of any type. For more information about the drawbacks of structured exception handling, see Structured Exception Handling.

In this section

See also

C++ Language Reference
x64 exception handling
Exception Handling (C++/CLI and C++/CX)

Modern C++ best practices for exceptions and error handling

 

In this article

  1. Use exceptions for exceptional code
  2. Basic guidelines
  3. Exceptions and performance
  4. Exceptions versus assertions

In modern C++, in most scenarios, the preferred way to report and handle both logic errors and runtime errors is to use exceptions. It's especially true when the stack might contain several function calls between the function that detects the error, and the function that has the context to handle the error. Exceptions provide a formal, well-defined way for code that detects errors to pass the information up the call stack.

Use exceptions for exceptional code

Program errors are often divided into two categories: Logic errors that are caused by programming mistakes, for example, an "index out of range" error. And, runtime errors that are beyond the control of programmer, for example, a "network service unavailable" error. In C-style programming and in COM, error reporting is managed either by returning a value that represents an error code or a status code for a particular function, or by setting a global variable that the caller may optionally retrieve after every function call to see whether errors were reported. For example, COM programming uses the HRESULT return value to communicate errors to the caller. And the Win32 API has the GetLastError function to retrieve the last error that was reported by the call stack. In both of these cases, it's up to the caller to recognize the code and respond to it appropriately. If the caller doesn't explicitly handle the error code, the program might crash without warning. Or, it might continue to execute using bad data and produce incorrect results.

Exceptions are preferred in modern C++ for the following reasons:

  • An exception forces calling code to recognize an error condition and handle it. Unhandled exceptions stop program execution.

  • An exception jumps to the point in the call stack that can handle the error. Intermediate functions can let the exception propagate. They don't have to coordinate with other layers.

  • The exception stack-unwinding mechanism destroys all objects in scope after an exception is thrown, according to well-defined rules.

  • An exception enables a clean separation between the code that detects the error and the code that handles the error.

The following simplified example shows the necessary syntax for throwing and catching exceptions in C++.

C++
#include <stdexcept>
#include <limits>
#include <iostream>

using namespace std;

void MyFunc(int c)
{
    if (c > numeric_limits< char> ::max())
        throw invalid_argument("MyFunc argument too large.");
    //...
}

int main()
{
    try
    {
        MyFunc(256); //cause an exception to throw
    }

    catch (invalid_argument& e)
    {
        cerr << e.what() << endl;
        return -1;
    }
    //...
    return 0;
}

Exceptions in C++ resemble ones in languages such as C# and Java. In the try block, if an exception is thrown it will be caught by the first associated catch block whose type matches that of the exception. In other words, execution jumps from the throw statement to the catch statement. If no usable catch block is found, std::terminate is invoked and the program exits. In C++, any type may be thrown; however, we recommend that you throw a type that derives directly or indirectly from std::exception. In the previous example, the exception type, invalid_argument, is defined in the standard library in the <stdexcept> header file. C++ doesn't provide or require a finally block to make sure all resources are released if an exception is thrown. The resource acquisition is initialization (RAII) idiom, which uses smart pointers, provides the required functionality for resource cleanup. For more information, see How to: Design for exception safety. For information about the C++ stack-unwinding mechanism, see Exceptions and stack unwinding.

Basic guidelines

Robust error handling is challenging in any programming language. Although exceptions provide several features that support good error handling, they can't do all the work for you. To realize the benefits of the exception mechanism, keep exceptions in mind as you design your code.

  • Use asserts to check for errors that should never occur. Use exceptions to check for errors that might occur, for example, errors in input validation on parameters of public functions. For more information, see the Exceptions versus assertions section.

  • Use exceptions when the code that handles the error is separated from the code that detects the error by one or more intervening function calls. Consider whether to use error codes instead in performance-critical loops, when code that handles the error is tightly coupled to the code that detects it.

  • For every function that might throw or propagate an exception, provide one of the three exception guarantees: the strong guarantee, the basic guarantee, or the nothrow (noexcept) guarantee. For more information, see How to: Design for exception safety.

  • Throw exceptions by value, catch them by reference. Don't catch what you can't handle.

  • Don't use exception specifications, which are deprecated in C++11. For more information, see the Exception specifications and noexcept section.

  • Use standard library exception types when they apply. Derive custom exception types from the exception Class hierarchy.

  • Don't allow exceptions to escape from destructors or memory-deallocation functions.

Exceptions and performance

The exception mechanism has a minimal performance cost if no exception is thrown. If an exception is thrown, the cost of the stack traversal and unwinding is roughly comparable to the cost of a function call. Additional data structures are required to track the call stack after a try block is entered, and additional instructions are required to unwind the stack if an exception is thrown. However, in most scenarios, the cost in performance and memory footprint isn't significant. The adverse effect of exceptions on performance is likely to be significant only on memory-constrained systems. Or, in performance-critical loops, where an error is likely to occur regularly and there's tight coupling between the code to handle it and the code that reports it. In any case, it's impossible to know the actual cost of exceptions without profiling and measuring. Even in those rare cases when the cost is significant, you can weigh it against the increased correctness, easier maintainability, and other advantages that are provided by a well-designed exception policy.

Exceptions versus assertions

Exceptions and asserts are two distinct mechanisms for detecting run-time errors in a program. Use assert statements to test for conditions during development that should never be true if all your code is correct. There's no point in handling such an error by using an exception, because the error indicates that something in the code has to be fixed. It doesn't represent a condition that the program has to recover from at run time. An assert stops execution at the statement so that you can inspect the program state in the debugger. An exception continues execution from the first appropriate catch handler. Use exceptions to check error conditions that might occur at run time even if your code is correct, for example, "file not found" or "out of memory." Exceptions can handle these conditions, even if the recovery just outputs a message to a log and ends the program. Always check arguments to public functions by using exceptions. Even if your function is error-free, you might not have complete control over arguments that a user might pass to it.

C++ exceptions versus Windows SEH exceptions

Both C and C++ programs can use the structured exception handling (SEH) mechanism in the Windows operating system. The concepts in SEH resemble the ones in C++ exceptions, except that SEH uses the __try__except, and __finally constructs instead of try and catch. In the Microsoft C++ compiler (MSVC), C++ exceptions are implemented for SEH. However, when you write C++ code, use the C++ exception syntax.

For more information about SEH, see Structured Exception Handling (C/C++).

Exception specifications and noexcept

Exception specifications were introduced in C++ as a way to specify the exceptions that a function might throw. However, exception specifications proved problematic in practice, and are deprecated in the C++11 draft standard. We recommend that you don't use throw exception specifications except for throw(), which indicates that the function allows no exceptions to escape. If you must use exception specifications of the deprecated form throw( type-name ), MSVC support is limited. For more information, see Exception Specifications (throw). The noexcept specifier is introduced in C++11 as the preferred alternative to throw().

See also

How to: Interface between exceptional and non-exceptional code
C++ language reference
C++ Standard Library

 

 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章