C++ Exceptions: try, catch and throw
An exception is a way to report an error. When code throws, the normal flow stops, and C++ goes up the calls until a matching catch.
#include <iostream>
#include <stdexcept>
double divide(double a, double b) {
if (b == 0) throw std::invalid_argument("division by zero");
return a / b;
}
int main() {
try {
std::cout << divide(10, 2) << '\n';
std::cout << divide(1, 0) << '\n';
std::cout << "not printed\n";
} catch (const std::invalid_argument& e) {
std::cout << "Caught: " << e.what() << '\n';
}
std::cout << "the program goes on\n";
}
5
Caught: division by zero
the program goes on
Catch by const reference
Always catch by const&. Catching by value makes a copy and can slice the exception. Many catches are checked in order, and catch (...) catches everything.
#include <iostream>
#include <stdexcept>
#include <string>
#include <vector>
int main() {
try {
std::vector<int> v{1, 2, 3};
std::cout << v.at(10);
} catch (const std::out_of_range& e) {
std::cout << "out_of_range\n";
}
try {
std::stoi("abc");
} catch (const std::invalid_argument&) {
std::cout << "invalid_argument\n";
}
try {
throw 42; // you can throw any type, but do not
} catch (...) {
std::cout << "something else\n";
}
}
out_of_range
invalid_argument
something else
Custom exception
#include <iostream>
#include <stdexcept>
#include <string>
class InsufficientFunds : public std::runtime_error {
public:
InsufficientFunds(const std::string& msg, double short_by)
: std::runtime_error(msg), short_by(short_by) {}
double short_by;
};
void withdraw(double balance, double amount) {
if (amount > balance) throw InsufficientFunds("Not enough money", amount - balance);
}
int main() {
try {
withdraw(500, 800);
} catch (const InsufficientFunds& e) {
std::cout << e.what() << ", short by " << e.short_by << '\n';
}
}
Not enough money, short by 300
RAII makes exceptions safe
When an exception leaves a block, C++ calls the destructors of all the objects of that block, so smart pointers, strings and files are cleaned.
#include <iostream>
#include <stdexcept>
struct Guard {
~Guard() { std::cout << "cleaned up\n"; }
};
int main() {
try {
Guard g;
throw std::runtime_error("boom");
} catch (const std::exception& e) {
std::cout << "caught " << e.what() << '\n';
}
}
cleaned up
caught boom
The order shows that cleaned up comes before caught: the stack is unwound first.
noexcept
noexcept promises that a function does not throw. If it does anyway, the program calls std::terminate. Move constructors should be noexcept.
std::expected (C++23)
For errors which are normal and expected (like a wrong input), you can return the result or the error, without exceptions:
#include <expected>
#include <iostream>
#include <string>
std::expected<int, std::string> parse(const std::string& s) {
try {
return std::stoi(s);
} catch (...) {
return std::unexpected("not a number: " + s);
}
}
int main() {
for (std::string text : {"42", "abc"}) {
auto r = parse(text);
if (r) std::cout << "value " << *r << '\n';
else std::cout << "error " << r.error() << '\n';
}
}
value 42
error not a number: abc
| Exercises & Assignments |
|---|
| No Content Found. |
| Interview Questions & Answers |
|---|
| No Content Found. |