Error Handling in C++: try/catch and Advanced Techniques

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Advanced Error Handling in C++: From try/catch to Industrial Practices



Error handling is one of the cornerstones of industrial programming. In C++, the developer has access not only to the classic try/catch mechanism, but also to a whole arsenal of advanced techniques: from RAII to exception safety guarantees. In this article, we will explore how to write robust C++ code that correctly handles failures, does not leak resources, and remains readable.



1. Basics and Common Pitfalls of try/catch



The basic syntax is familiar to everyone:



#include <iostream>#include <stdexcept>

void dangerousFunction(int value) { if (value < 0) { throw std::invalid_argument("Value must be non-negative"); } // ... work with value}

int main() { try { dangerousFunction(-5); } catch (const std::invalid_argument& e) { std::cerr << "Error: " << e.what() << std::endl; } catch (const std::exception& e) { std::cerr << "Generic error: " << e.what() << std::endl; } catch (...) { std::cerr << "Unknown exception caught" << std::endl; } return 0;}


Important points:

  • Catch by reference — catch (const std::exception& e) avoids object slicing.
  • The order of catch blocks matters: first more specific exceptions, then general ones.
  • Do not overuse catch (...) — it hides all errors, including critical ones (e.g., std::bad_alloc).
  • Never throw exceptions from destructors — this leads to undefined behavior (usually abnormal termination).


2. RAII as an Alternative to try/catch for Resource Management



RAII (Resource Acquisition Is Initialization) is a key C++ pattern that makes exceptions safe. The idea: a resource is acquired in the constructor and released in the destructor. If an exception occurs, the destructors of all created objects are called automatically (stack unwinding).



#include <fstream>#include <vector>#include <stdexcept>

void processFile(const std::string& filename) { std::ifstream file(filename); // RAII: file opens in the constructor if (!file.is_open()) { throw std::runtime_error("Cannot open file: " + filename); }

std::vector<int> data; int value; while (file >> value) { data.push_back(value); } // The file destructor will close the file automatically, // even if an exception occurs (e.g., bad_alloc during push_back)}


Advantages of RAII over manual try/catch:

  • No need to write finally blocks — destructors guarantee cleanup.
  • Code becomes more linear and readable.
  • Exceptions do not lead to leaks of memory, file descriptors, etc.


3. Exception Safety Guarantees



In C++, it is common to distinguish three levels of guarantees for functions:



  • No-throw guarantee — the function never throws exceptions. Marked with noexcept.
  • Strong guarantee — if an exception occurs, the program state remains unchanged (atomic operation).
  • Basic guarantee — upon an exception, no resources are leaked, objects remain in a consistent state, but their value may change.


Example with strong guarantee (copy-and-swap idiom):



#include <algorithm>  // for std::swap#include <vector>#include <stdexcept>

class SafeVector { std::vector<int> data_;public: void push_back(int value) { // 1. Create a copy

Blogs

Book Recommendations