Exceptions in C++

Written by

Ananya Pandey

Say you are working on creating a calculator using C++, you successfully compile it with no errors and choose to divide a number with the divisor being 0 and your program simply terminates with no output.

Why does that happen? Because of Exceptions.

Exceptions are temporary objects of any data type, usually of the class data type, and are used to signal errors during run-time. Basically a runtime error, exceptions are still different from errors as they merely indicate abnormalities with the code and not serious problems that a coder should not try to catch. Exceptions can occur anywhere in a program despite it running correctly, operations like object creation, data input, and output are all potential exception causers.

Exceptions are of two types:

  • Synchronous exceptions: These exceptions occur during runtime as a result of faulty or inconsistent input. Examples include Division by zero, out of range, and overflow errors.
  • Asynchronous exceptions: These exceptions are beyond the control of the code and have nothing to do with the program alone, external factors are mainly responsible for such exceptions. Example: Processor interrupts, keyboard failures, and hardware malfunction in general.

Hence, in order to build a robust program, exception handling is necessary. Handling exceptions produce cleaner and simpler codes that are less likely to run into errors.

Fairly a new feature, exception handling involves signaling the error by throwing an exception in the code and shifting the control to special handler functions that execute the exceptional code.

C++ uses three keywords for handling exceptions. They are: try, throw and catch.

try: The try keyword allows the programmer to define a block of code that is to be inspected for errors or exceptions and also invokes the code that throws an exception using the throw keyword.

Simply put, the code enclosed in the try block is executed normally and only throws the exception when needed.

If a statement that is not within a try block throws an exception or a statement within a try block throws an exception that is not caught, the program terminates.

throw: The throw keyword is used in the try block to invoke an exception when an issue is detected. It is the throw statement that essentially transfers the flow of control to the handler block to continue the proper functioning of the program.

The throw statement accepts just one parameter which can optionally be enclosed in parentheses. This parameter is then passed as an argument to the exception handler block i.e, the catch block.

Once the throw statement is executed, the control does not go back to the function resulting in unwinding of the stack.

catch: Immediately followed by the try block, a catch block contains code to handle a particular kind of exception and process it. A program can contain one or more catch blocks depending on the throw statements i.e, a catch block can be overloaded. The argument passed to the catch block can belong to any data type.

In case of catch overloading, only the block matching the parameter supplied by throw is executed.

As the catch block contains the code that deals with errors caused by exceptions, it is also referred to as an exception handler

If no exception is caught, the catch block is ignored, and if a parameter mismatch is found, the program is terminated.

Hence the exception handling routine of C++ is quite similar to that of other languages like Java, with the routine being:

  1. Detecting the issue (using the try block)
  2. Notifying the code that an exception has occurred(using throw) and stack is unwinded
  3. Receiving the exception information by the catch block
  4. Handling the exception.

//A blueprint of error handling code in C++

try {

// error prone code with or without condition

throw exception;

}

catch (<datatype> exception)

{

// code to be executed in case of exception

}

Let us now look back at our introductory conundrum of dividing a number by zero and fix it with the exception handling technique:


// A snippet of the division function code

float a, b;

cout << "Enter the values of a and b" << endl;

cin >> a >> b;

try {

  if (b != 0)

    return a / b; //Executed if no exceptions encountered

  else

    throw b; //throwing exception with parameter ‘b’

} catch (float i){ //Receiving ‘b’ as an argument

  //Handling exception

  cout << "Division by zero not possible.\n Please try again... " << endl;

}

return 0;

}

This way, the calculator program shall not terminate and function properly.

Output:


Enter the values of a and b

20

0

Division by zero not possible

Please try again...

Standard C++ Exceptions:

The C++ Standard Library provides a specially designed base class of exceptions that can be captured. It is called std::exception and is defined in the  header file. This class has an accessor function called what that returns a null sequence of type char * which can be overwritten and customized in user-derived classes.

One can throw exceptions of standard types in their code or use a standard exception class as a base to easily derive their own exception classes.

One can use the standard exception types in two ways: either throw exceptions of standard types in the code or use a standard exception class as a base for their own exception types.

There are nine standard exceptions in c++. They are bad_alloc, bad_cast, bad_exception,bad_function_call,bad_typeid,bad_weak_ptr, ios_base::failure, logic_error and runtime_error.

Nested try block:

A try block can be nested inside another try block. Each try block has its own set of catch blocks to handle exceptions that may be thrown within it, and the catch blocks for a try block are invoked for exceptions thrown only within that try block.

Eg:


try{ // outer try block

...

try{

// inner try block

...

}

catch (ExceptionType1 ex){ //This block catches exceptions thrown in the inner try block.


...

}

...

}

catch (ExceptionType2 ex) { ---// catches exceptions thrown in the outer try block, as well as uncaught exceptions of that type from the inner try block

...

}

Rethrowing Exceptions: A catch block can rethrow an exception to allow the catch block for an outer try block to catch it.

The syntax for rethrowing an exception just consists of the throw keyword: throw;

This statement rethrows an exception without copying it so that it can be caught by a

catch block for the outer try block.

Points to remember while using Exception handling in C++:

  1. The catch block must be right after the try block.
  2. An exception object must belong to the public class. An object of a class type that has a private, protected, or deleted copy constructor can’t be used as an exception.
  3. Braces ‘{ }’ are mandatory in the try/catch block even if they are single statements.
  4. The golden rule of exceptions: Always throw by value and catch by reference.

In this way, exceptional handling makes the code robust and more user-friendly and offers many advantages over traditional error handling techniques.

The major advantage being the fact that the error-handling code is separated completely from the error-causing code which increases readability and ease of maintenance.

Additionally, it has a substantial impact on adding functionality to an application.

The C++ language and Standard Library provide a rich, flexible, and powerful way to write code. It proves that knowledge of exception handling not only makes the program robust but also its programmer.

Exceptions in C++