throw signals an error that the current function can't handle; the stack unwinds (destroying locals) until a matching catch is found.
throw / try / catch
#include <iostream>
#include <stdexcept>
#include <string>
double divide(double a, double b) {
if (b == 0) throw std::invalid_argument("division by zero");
return a / b;
}
int parseAge(const std::string& s) {
int age = std::stoi(s); // may throw invalid_argument / out_of_range
if (age < 0 || age > 150) throw std::out_of_range("age " + s + " out of range");
return age;
}
int main() {
try {
std::cout << divide(10, 4) << '\n';
std::cout << divide(1, 0) << '\n'; // throws — the rest of the try is skipped
std::cout << "never printed\n";
} catch (const std::invalid_argument& e) {
std::cout << "invalid: " << e.what() << '\n';
}
for (std::string input : {"42", "abc", "200"}) {
try {
std::cout << parseAge(input) << '\n';
} catch (const std::out_of_range& e) { // most specific first
std::cout << "range: " << e.what() << '\n';
} catch (const std::exception& e) { // any standard exception
std::cout << "error: " << e.what() << '\n';
} catch (...) { // anything at all
std::cout << "unknown\n";
}
}
}2.5
invalid: division by zero
42
error: stoi: no conversion
range: age 200 out of rangeThe standard hierarchy
| Exception | Thrown by / used for |
|---|---|
std::exception | Base of all — has virtual const char* what() |
std::logic_error → invalid_argument, out_of_range, length_error, domain_error | Bugs detectable before running: bad arguments, vector::at, stoi |
std::runtime_error → range_error, overflow_error, system_error | Errors only detectable at run time: I/O, OS |
std::bad_alloc | new failed |
std::bad_cast, std::bad_optional_access, std::bad_variant_access, std::bad_function_call | Misused library types |
Custom exceptions & rethrowing
#include <iostream>
#include <stdexcept>
#include <string>
class HttpError : public std::runtime_error {
public:
HttpError(int code, const std::string& msg)
: std::runtime_error(msg), code_(code) {}
int code() const noexcept { return code_; }
private:
int code_;
};
void fetch() { throw HttpError(404, "page not found"); }
void handler() {
try {
fetch();
} catch (const HttpError& e) {
std::cout << "log: " << e.code() << '\n';
throw; // rethrow the SAME exception object
}
}
int main() {
try { handler(); }
catch (const std::exception& e) { std::cout << "top level: " << e.what() << '\n'; }
}log: 404
top level: page not foundStack unwinding
#include <iostream>
#include <stdexcept>
struct Guard {
const char* name;
~Guard() { std::cout << "cleanup " << name << '\n'; }
};
void inner() { Guard g{"inner"}; throw std::runtime_error("fail"); }
void outer() { Guard g{"outer"}; inner(); }
int main() {
try { outer(); } catch (const std::exception& e) { std::cout << "caught " << e.what() << '\n'; }
}cleanup inner
cleanup outer
caught failWhat happens when an exception is thrown?
Exceptions vs error codes?