Show Lecture.ExceptionHandling as a slide show.
try { … }
throw
an exception.
throw
value;
catch
clause. If no catch
clause applies,
then the program dies.
catch(
specification) { … }
void snap() { cout << "mind\n"; throw "Thanos"; cout << "power\n"; // 💀💀💀 } int main() { cout << "reality\n"; try { cout << "soul\n"; snap(); cout << "space\n"; // 💀💀💀 } catch (const char *villain) { cout << "Caught " << villain << '\n'; } cout << "time\n"; }
reality soul mind Caught Thanos time
throw
without catch
If something is thrown but never caught, then the special function
terminate()
is called, which complains in an implementation-defined manner,
and stops the program by calling abort()
.
throw "ouch";
terminate called after throwing an instance of 'char const*' SIGABRT: Aborted
#include "Loud.h" Loud a('a'); void foo() { Loud b('b'); Loud c('c'); } int main() { Loud d('d'); foo(); Loud e('e'); return 0; }
Loud::Loud() [c='a'] Loud::Loud() [c='d'] Loud::Loud() [c='b'] Loud::Loud() [c='c'] Loud::~Loud() [c='c'] Loud::~Loud() [c='b'] Loud::Loud() [c='e'] Loud::~Loud() [c='e'] Loud::~Loud() [c='d'] Loud::~Loud() [c='a']
throw
without catch
#include "Loud.h" Loud a('a'); void foo() { Loud b('b'); throw 42; Loud c('c'); } int main() { Loud d('d'); foo(); Loud e('e'); return 0; }
Loud::Loud() [c='a'] Loud::Loud() [c='d'] Loud::Loud() [c='b'] terminate called after throwing an instance of 'int' SIGABRT: Aborted
You can throw
anything—doesn’t have to be a special type or object.
Really—you can throw
and catch
any type. For example:
throw 42;
terminate called after throwing an instance of 'int' SIGABRT: Aborted
throw "alpha";
terminate called after throwing an instance of 'char const*' SIGABRT: Aborted
throw "beta"s;
terminate called after throwing an instance of 'std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >' SIGABRT: Aborted
int n=42; throw "Value no good: " + to_string(n);
terminate called after throwing an instance of 'std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >' SIGABRT: Aborted
throw logic_error("That failed miserably");
terminate called after throwing an instance of 'std::logic_error' what(): That failed miserably SIGABRT: Aborted
range_error r("value too large"); throw r;
terminate called after throwing an instance of 'std::range_error' what(): value too large SIGABRT: Aborted
try
#1#include "Loud.h" Loud a('a'); void foo() { Loud b('b'); throw "oops!"; Loud c('c'); } int main() { Loud d('d'); try { foo(); } catch (const char *error) { cout << "Caught: " << error << "\n"; } Loud e('e'); return 0; }
Loud::Loud() [c='a'] Loud::Loud() [c='d'] Loud::Loud() [c='b'] Loud::~Loud() [c='b'] Caught: oops! Loud::Loud() [c='e'] Loud::~Loud() [c='e'] Loud::~Loud() [c='d'] Loud::~Loud() [c='a']
try
#2#include "Loud.h" Loud a('a'); void foo() { Loud b('b'); throw "oops!"; Loud c('c'); } void bar() { Loud d('d'); foo(); } int main() { Loud e('e'); try { bar(); } catch (const char *error) { cout << "Caught: “" << error << "”\n"; } Loud f('f'); return 0; }
Loud::Loud() [c='a'] Loud::Loud() [c='e'] Loud::Loud() [c='d'] Loud::Loud() [c='b'] Loud::~Loud() [c='b'] Loud::~Loud() [c='d'] Caught: “oops!” Loud::Loud() [c='f'] Loud::~Loud() [c='f'] Loud::~Loud() [c='e'] Loud::~Loud() [c='a']
catch
#1try { throw "oops!"; } catch (int i) { cout << "int " << i << "\n"; } catch (const char *error) { cout << "C string: " << error << "\n"; } catch (...) { // Gotta catch ’em all! cout << "something\n"; }
C string: oops!
catch
clauses.
catch
the most specific things first,
and the least specific things last.
catch (...)
is the least specific of all, so it must go last.
catch
;
don’t count on it.
try { } catch (...) { } catch (int) { }
c.cc:2: error: '...' handler must be the last handler for its try block
catch
#2try { throw 42; } catch (short s) { cout << "Got a short: " << s << "\n"; } catch (long l) { cout << "Got a long: " << l << "\n"; }
terminate called after throwing an instance of 'int' SIGABRT: Aborted
The type must match. No conversions!
catch
#2½try { throw "Son of a gun!"; } catch (string s) { cout << "Got a string: " << s << "\n"; }
terminate called after throwing an instance of 'char const*' SIGABRT: Aborted
Why didn’t that work?
catch
#2¾try { throw "That’s better."s; } catch (string s) { cout << "Got a string: " << s << "\n"; }
Got a string: That’s better.
Hey, that worked!
catch
#2⅞try { throw "Even better."s; } catch (const string &s) { cout << "Got a string: " << s << "\n"; }
Got a string: Even better.
This works, and is more efficient. Of course, efficiency may not be an issue here.
catch
#3// logic_error is-a exception // runtime_error is-a exception // overflow_error is-a runtime_error try { throw overflow_error("Just too big!"); } catch (const logic_error &e) { cout << "logic_error: " << e.what() << '\n'; } catch (const runtime_error &e) { cout << "runtime_error: " << e.what() << '\n'; } catch (const exception &e) { cout << "exception: " << e.what() << '\n'; }
runtime_error: Just too big!
catch
#4// logic_error is-a exception // runtime_error is-a exception // overflow_error is-a runtime_error try { throw overflow_error("Just too big!"); } catch (const logic_error &e) { cout << "logic_error: " << e.what() << '\n'; } catch (const exception &e) { cout << "exception: " << e.what() << '\n'; }
exception: Just too big!
No conversions, but is-a is good enough.
throw
void foo() { throw string("Division by zero"); } void bar() { try { foo(); } catch (string msg) { if (msg == "Out of memory") // I’ve got this! /* get more memory */; else throw; // Throw up hands in despair. } } int main() { try { bar(); } catch (string problem) { cout << "Problem in bar: " << problem << '\n'; } return 0; }
Problem in bar: Division by zero