3.9. Exception handling

An alternative to returning values from functions is to use exceptions.

A C++ exception is a response to an exceptional circumstance that occurs while a program is running, such as an attempt to open a file that does not exist.

Exceptions provide a way to transfer control from one part of a program (where the error occurs) to another (where the error is ‘handled’). C++ exception handling is built upon three keywords: try, catch, and throw.

throw

A program throws an exception when a problem shows up.

try

A throws block identifies a block of code for which particular exceptions will be activated. It’s followed by one or more catch blocks.

catch

A program catches an exception with an exception handler at the place in a program where you want to handle the problem.

finally

The finally block allows the code to clean up after resources regardless of what exception may have been caught.

Basic anatomy of an exception

  1. Surround potentially error throwing code in a try block

  2. After the try block, include 1 or more catch blocks

    1. The parameter in the catch identifies the type of exception the catch block will handle

try {
  // execute potentially dangerous statements
}

// catch a specific class of exceptions
catch (const std::exception& e) {
  std::cout << "Exception occurred.\n";
  std::cout << "Details: " << e.what() << std::endl;
}

If you specify a try, you must include at least 1 catch.

Passing catch parameters by const reference is considered a best practice

will catch any exception. If you use this, then you should be prepared to really handle anything.

3.9.1. Standard exceptions

The standard exceptions in C++ are organized in a class hierarchy.

3.9.2. Using exceptions

C++ exceptions are designed to support error handling.

Use throw only to signal an error. Use catch only to specify error handling actions when you know you can handle it. Possibly by translating it to another type and re-throwing an exception of that type. For example, catching a bad_alloc and re-throwing a no_space_for_file_buffers exception.

Do not use throw to catch a coding error in usage of a function. Instead, use assert or other mechanism to either stop the program or log the error.

Do not use throw if you discover an unexpected violation of an invariant of your component. Instead, use assert or other mechanism to terminate the program. Throwing an exception will not cure memory corruption and may lead to further corruption of important user data.

Use try and catch blocks if the logic is more clear than checking a condition and returning a value. For example, if you need to propagate errors several levels up the stack:

void f1() {
  try {
    f2();
  } catch (const some_exception& e) {
    // ... handle error
  }
}
void f2() { ...; f3(); ...; }
void f3() { ...; f4(); ...; }
void f4() { ...; f5(); ...; }
void f5()
{
  if ( /*...some error condition...*/ )
    throw some_exception();
}

Only the code that detects the error, f5(), and the code that handles the error, f1(), have any clutter. None of the other functions have to worry about passing error codes either in return values or in extra parameters that would have to be mutable.

Do not use try blocks to reclaim resources. This is a Java technique, which is great for Java, but is not needed in C++. In C++, use Resource Acquisition Is Initialization (RAII).

Use constructors to allocate resources and use destructors to clean up resources,

Do not use try blocks as a proxy for error return codes. This results in too many try blocks cluttering up functions, which harms readability if nothing else.

3.9.3. Exceptions and I/O streams

I/O streams can be configured to throw exceptions with std::basic_ios::exceptions. This object gets and sets the exception mask of the stream. The exception mask set in the program determines which error states in the stream will throw an exception if an error is encountered. If no exception bits are set, then the I/O streams in C++ will not throw any exceptions.

For example:

std::ifstream ifs("in.txt");
ifs.exceptions(std::ifstream::failbit);

At this point, only the failbit will trigger an exception.

I/O Streams may throw ios_base::failure But since C++11 this class inheritance changed.

ios_base::failure inherits from std::system_error

The end result is that ios_base::failure now has an error_code member to the exception object it didn’t used to have.

catch (const ios_base::failure& e) {
  std::cout << "I/O exception occurred.\n";
  std::cout << "Details: " << e.what() << std::endl;
  std::cout << "Code: " << e.code() << std::endl;
}

You have attempted of activities on this page