Expert · Exceptions, concurrency and building projects with CMake · topic 34 of 42 · 30 min
Exceptions: throw, try, catch and noexcept
Explanation
Why exceptions?
Errors happen: a file is missing, a network call times out, a user types letters for an amount. Returning error codes means every caller must check every result, and it is easy to forget. An exception separates error handling from normal code: the function that detects a problem throws, and a function higher up that knows what to do catches. Constructors, which have no return value, can only report failure this way.
#include <iostream>
#include <stdexcept>
#include <string>
double withdraw(double balance, double amount) {
if (amount <= 0) throw std::invalid_argument("amount must be positive");
if (amount > balance) throw std::runtime_error("insufficient balance");
return balance - amount;
}
int main() {
try {
double left = withdraw(500, 800);
std::cout << "Left: " << left << '\n'; // skipped
} catch (const std::invalid_argument& e) {
std::cout << "Bad input: " << e.what() << '\n';
} catch (const std::exception& e) { // any other standard exception
std::cout << "Error: " << e.what() << '\n'; // Error: insufficient balance
}
}How throw and catch work
- throw creates an exception object and starts searching for a handler.
- The current function stops. Its local objects are destroyed in reverse order (stack unwinding), so RAII destructors run.
- The search moves up to the caller, then its caller, until it finds a try block with a matching catch.
- The catch block runs. The program continues after that try/catch.
- If no catch matches anywhere, std::terminate is called and the program ends.
The standard exception family
| Type (in <stdexcept> unless noted) | Typical use |
|---|---|
| std::exception (<exception>) | base class of all standard exceptions; has what() |
| std::logic_error, std::invalid_argument, std::out_of_range | bugs or bad arguments: v.at(99), std::stoi("abc") |
| std::runtime_error | problems only visible at run time: a file is missing, a server failed |
| std::bad_alloc (<new>) | new could not get memory |
class PaymentError : public std::runtime_error {
public:
PaymentError(const std::string& msg, int code)
: std::runtime_error(msg), code_(code) {}
int code() const noexcept { return code_; }
private:
int code_;
};
try {
throw PaymentError("gateway timeout", 504);
} catch (const PaymentError& e) {
std::cout << e.what() << " (" << e.code() << ")\n";
}Rules of good exception use
- Throw by value (throw std::runtime_error("...")), catch by const reference (catch (const std::exception& e)). Catching by value slices derived exceptions.
- Order catch blocks from most specific to most general; the first match wins.
- catch (...) catches anything; use it only to log and rethrow (throw;) or at the top of main.
- Use exceptions for exceptional situations, not normal control flow such as "end of loop".
- Never let an exception escape a destructor.
noexcept
Marking a function noexcept promises it will not throw. If it throws anyway, std::terminate is called. Destructors are noexcept by default. Mark move constructors, move assignments and swap functions noexcept: as you saw, std::vector uses moves only when they are noexcept.
Need help?
Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.
Try:
Watch the lesson
Exceptions: throw, try, catch and noexcept · 1/6
Exceptions report errors that the current function cannot handle.
Prefer a video by a teacher? Find free videos on “c++ exceptions try catch throw noexcept stack unwinding tutorial” on YouTube ↗
In real life: Reading a configuration file at startup
A server reads settings like the port number from a file when it starts. If the file is missing or the port is not a number, it is pointless to continue. The loader throws, main catches the exception, prints a clear message for the operations team, and exits with a non-zero code so the deployment system notices.
#include <fstream>
#include <iostream>
#include <stdexcept>
#include <string>
int loadPort(const std::string& path) {
std::ifstream in(path);
if (!in) throw std::runtime_error("cannot open " + path);
std::string line;
std::getline(in, line);
int port = std::stoi(line); // may throw std::invalid_argument
if (port < 1 || port > 65535) throw std::out_of_range("port out of range");
return port;
}
int main() {
try {
std::cout << "Listening on " << loadPort("server.conf") << '\n';
} catch (const std::exception& e) {
std::cerr << "Startup failed: " << e.what() << '\n';
return 1;
}
}Practice (0/4 done)
What prints? try { throw std::runtime_error("x"); std::cout << "A"; } catch (const std::exception&) { std::cout << "B"; } std::cout << "C";
Which catch order is correct?
What happens if an exception is thrown and nothing catches it?
Write int parseAge(const std::string& s) that throws std::invalid_argument for non-numbers (let std::stoi do it) and std::out_of_range with your own message for ages outside 0–130. In main, read ages in a loop, catch each kind separately, and print a friendly message without stopping the loop.
Quick quiz
Read more (free)
Related help
Before this, learn
This topic builds on these. If something here feels new, open them first.
Related topics
- Concurrency 1: threads, mutexes and data racesExceptions, concurrency and building projects with CMake · same module
- Concurrency 2: atomics, async, futures and condition variablesExceptions, concurrency and building projects with CMake · same module
- Building real projects: CMake, warnings, debuggers and sanitizersExceptions, concurrency and building projects with CMake · same module
- Exceptions: try, catch, finally and custom exceptionsC# and .NET · Exceptions, delegates, events and async
- Exceptions: try, catch and finallyJava Programming (Core to Spring Boot) · Exceptions and generics
- Error handling: try, catch, finally and throwJavaScript Programming (language in depth) · Modules, errors, JSON and dates
Questions people ask about this
Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.
What is stack unwinding?
Answer: Destroying local objects in each function as an exception travels up to a handler
It is what makes RAII cleanup run during exceptions.
Why catch by const reference instead of by value?
Answer: It avoids copying and avoids slicing derived exception types
Catching by value copies and slices.
A noexcept function throws. What happens?
Answer: std::terminate is called
Breaking a noexcept promise ends the program.
Which base class should your own exception types usually derive from?
Answer: std::exception (often via std::runtime_error)
Then generic handlers for std::exception catch them too, and what() works.
What happens to an exception that nobody catches?Related
Answer: The program crashes and shows the error
Unhandled exceptions end the process.
What happens to an exception nobody catches in main?Related
Answer: The thread ends and the stack trace is printed
Uncaught exceptions end the thread.
Why throw new Error("…") instead of throw "…"?Related
Answer: Error objects carry a name and a stack trace
Strings can be thrown but have no stack, which makes debugging hard.
Why should move constructors be noexcept?Related
Answer: So std::vector moves elements when it grows instead of copying them
vector only uses a move if it cannot throw, to keep its exception guarantee.
Pass the quiz (60% or more) or mark this topic as done when you are ready.
Your progress is saved on this device only.
Next steps
- 3 more topics in “Exceptions, concurrency and building projects with CMake” before its exam: Concurrency 1: threads, mutexes and data races, Concurrency 2: atomics, async, futures and condition variables, Building real projects: CMake, warnings, debuggers and sanitizers.
- Module 9 exam: Exceptions, concurrency and CMake · 12 questions · 25 min, without help. 4 topics of this module still to finish first.
- Then the next module, Competitive programming, interviews and careers. 42 topics left in the course before the final exam.