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.