Throw and Catch Exceptions
beginner
c++11
In C++ to raise or throw exceptions the throw
keyword is used.
To catch exceptions the try
and catch
keywords are used.
The C++ Standard Library provides many basic exception types in the <exception>
header.
All Standard Library exceptions inherit from std::exception
which provides the what()
method that returns an explanatory string of why the exception was thrown.
Here’s an example of a program that asserts we don’t accidentally divide by 0 and throws a std::invalid_argument
if we attempt to:
#include <exception> #include <iostream> float strict_divide(float dividend, float divisor) { if (divisor == 0.0f) { throw std::invalid_argument("Cannot divide by zero"); } return dividend / divisor; } int main() { try { strict_divide(42, 0); } catch (const std::invalid_argument& e) { std::cout << e.what() << "\n"; } }
Cannot divide by zero
Catching Different Exception Types
Multiple catch
statements can be used to catch exceptions of different types.
#include <exception> #include <cmath> #include <iostream> float strict_divide(float dividend, float divisor) { if (!std::isfinite(dividend)) { throw std::out_of_range("Dividend isn't finite"); } else if (!std::isnormal(divisor)) { throw std::invalid_argument("Divisor isn't normal"); } return dividend / divisor; } int main() { try { strict_divide(INFINITY, 2); } catch (const std::out_of_range& e) { std::cout << "Out of Range: " << e.what() << "\n"; } catch (const std::invalid_argument& e) { std::cout << "Invalid Argument: " << e.what() << "\n"; } }
Out of Range: Dividend isn't finite
Or, you can catch std::exception
to catch any exception from the standard library.
#include <exception> #include <cmath> #include <iostream> float strict_divide(float dividend, float divisor) { if (!std::isfinite(dividend)) { throw std::out_of_range("Dividend isn't finite"); } else if (!std::isnormal(divisor)) { throw std::invalid_argument("Divisor isn't normal"); } return dividend / divisor; } int main() { try { strict_divide(42, 0); } catch (const std::exception& e) { std::cout << e.what() << "\n"; } }
Divisor isn't normal
You an also use catch(...)
as a “catch anything” statement, ignoring the exception type and message.
#include <exception> #include <cmath> #include <iostream> float strict_divide(float dividend, float divisor) { if (!std::isfinite(dividend)) { throw std::out_of_range("Dividend isn't finite"); } else if (!std::isnormal(divisor)) { throw std::invalid_argument("Divisor isn't normal"); } return dividend / divisor; } int main() { try { strict_divide(INFINITY, 2); } catch (...) { std::cout << "Something went wrong\n"; } }
Something went wrong
Custom Exception Types
Custom exception types are easy as exceptions are just classes. Exeptions can inherit from Standard Library exceptions as well.
#include <exception> #include <cmath> #include <iostream> class bad_divide : public std::domain_error { public: bad_divide(const char* what, float dividend, float divisor) : std::domain_error(what), dividend(dividend), divisor(divisor) { } float dividend; float divisor; }; float strict_divide(float dividend, float divisor) { if (!std::isfinite(dividend)) { throw bad_divide("Dividend isn't finite", dividend, divisor); } else if (!std::isnormal(divisor)) { throw bad_divide("Divisor isn't normal", dividend, divisor); } return dividend / divisor; } int main() { try { strict_divide(INFINITY, 2); } catch (const bad_divide& e) { std::cout << e.what() << ": " << e.dividend << " / " << e.divisor << "\n"; } }
Dividend isn't finite: inf / 2