Exceptions and RAII

throw and catch, and how destructors clean up for you.

C++
#include <iostream>
#include <stdexcept>
#include <string>
using namespace std;

int parseMarks(const string &text) {
  int m = stoi(text);
  if (m < 0 || m > 100) {
    throw out_of_range("marks must be 0 to 100");
  }
  return m;
}

class Guard {
  string name;

public:
  explicit Guard(string n) : name(std::move(n)) { cout << "open " << name << "\n"; }
  ~Guard() { cout << "close " << name << "\n"; }
};

int main() {
  for (string s : {"85", "140", "abc"}) {
    try {
      Guard g(s);
      cout << parseMarks(s) << "\n";
    } catch (const out_of_range &e) {
      cout << "Error: " << e.what() << "\n";
    } catch (const invalid_argument &) {
      cout << "Error: not a number\n";
    }
  }
  return 0;
}
Output
open 85
85
close 85
open 140
close 140
Error: marks must be 0 to 100
open abc
close abc
Error: not a number

When an exception is thrown, C++ leaves each block and runs the destructors of the objects inside it. That is why "close" prints before the error. Getting a resource in a constructor and releasing it in the destructor is called RAII.

string, vector, unique_ptr, ifstream and lock_guard all work this way, so in modern C++ you rarely free memory or close files by hand, even when errors happen.

  • Throw by value and catch by const reference.
  • Catch the most specific type first.
  • Do not use exceptions for normal events, such as reaching the end of the input.
โ† Lambdas and algorithmsC++ quick reference โ†’