Exceptions: throw, try and catch

Exception Safety

What is exception safety, and how can I ensure my code is exception-safe?

Abstract art representing computer programming

Exception safety refers to the ability of code to correctly handle and recover from exceptions without leaving the program in an inconsistent or invalid state. Exception-safe code ensures that resources are properly managed, invariants are maintained, and the program remains in a well-defined state, even in the presence of exceptions.

There are three commonly recognized levels of exception safety:

  1. Basic guarantee: If an exception is thrown, the program is left in a valid but unspecified state. No resources are leaked, and all invariants are preserved.
  2. Strong guarantee: If an exception is thrown, the program state remains unchanged, as if the operation had never been attempted. This is also known as "commit or rollback" semantics.
  3. No-throw guarantee: The operation is guaranteed not to throw any exceptions and always succeeds.

To ensure exception safety in your code, you can follow these guidelines:

  • Use RAII (Resource Acquisition Is Initialization) techniques to manage resources. This involves wrapping resource acquisition and release in classes with constructors and destructors, ensuring automatic cleanup in case of exceptions.
  • Use standard library containers and algorithms, which provide strong exception safety guarantees.
  • Use smart pointers (e.g., std::unique_ptrstd::shared_ptr) to manage dynamically allocated memory and avoid manual memory management.
  • Avoid throwing exceptions from destructors, as they may lead to unexpected behavior and resource leaks.
  • Use the "copy and swap" idiom for assignment operators to provide strong exception safety.
  • Use noexcept specifiers to indicate functions that are guaranteed not to throw exceptions.
  • Handle exceptions at the appropriate level of abstraction and avoid letting exceptions propagate uncontrolled.

Here's an example of exception-safe code using RAII:

#include <iostream>
#include <memory>

class Resource {
 public:
  Resource() {
    std::cout << "Acquiring resource\n";
  }
  ~Resource() {
    std::cout << "Releasing resource\n";
  }
};

void foo() {
  std::unique_ptr<Resource> resource{
    std::make_unique<Resource>()};

  // Use the resource
  throw std::runtime_error("Exception occurred");
}

int main() {
  try {
    foo();
  } catch (const std::exception& ex) {
    std::cout << "Caught exception: "
      << ex.what();
  }
}
Acquiring resource
Releasing resource
Caught exception: Exception occurred

In this example, the Resource class represents a resource that needs to be acquired and released. The std::unique_ptr is used to manage the lifetime of the resource. Even if an exception is thrown within the foo() function, the destructor of Resource will be called automatically when the std::unique_ptr goes out of scope, ensuring proper cleanup and preventing resource leaks.

By following exception safety principles and using appropriate techniques like RAII and smart pointers, you can write robust and exception-safe code in C++.

Answers to questions are automatically generated and may not have been reviewed.

A computer programmer
Part of the course:

Professional C++

Comprehensive course covering advanced concepts, and how to use them on large-scale projects.

Free, unlimited access

This course includes:

  • 125 Lessons
  • 550+ Code Samples
  • 96% Positive Reviews
  • Regularly Updated
  • Help and FAQ
Free, Unlimited Access

Professional C++

Comprehensive course covering advanced concepts, and how to use them on large-scale projects.

Screenshot from Warhammer: Total War
Screenshot from Tomb Raider
Screenshot from Jedi: Fallen Order
Contact|Privacy Policy|Terms of Use
Copyright © 2024 - All Rights Reserved