Exceptions and safety
Exceptions report failures, and RAII guarantees cleanup happens on the way out.
- Throw and catch exceptions only for genuinely exceptional failures.
- Release resources through destructors so cleanup happens automatically on unwind.
- Mark functions noexcept when they cannot throw.
- Aim for the strong guarantee: on failure, leave state unchanged.
In plain English
An exception is a way to report an error by unwinding the call stack until a matching catch block handles it. The try/catch pair marks the region where you expect a failure and the code that responds to it. RAII cleanup means resources such as files or locks are released by destructors automatically as the stack unwinds, so you do not need manual cleanup on every error path. Marking a function noexcept promises it will not throw, which lets the compiler and standard library optimise and reason about safety. The strong exception guarantee means that if an operation throws, the object is left exactly as it was before the operation began.
Step by step
- 1Wrap a risky operation in a try block.
- 2Write a catch block for std::exception or a more specific type.
- 3Put resources in a class whose destructor releases them.
- 4Mark a function noexcept when it is guaranteed not to throw.
- 5When a failure occurs, leave the object in its original state.
#include <fstream>
#include <iostream>
#include <stdexcept>
#include <string>
class NotesFile {
public:
explicit NotesFile(const std::string& path) : file_(path) {
if (!file_.is_open()) {
throw std::runtime_error("cannot open " + path);
}
}
std::string read_line() {
std::string line;
if (!std::getline(file_, line)) {
throw std::runtime_error("read failed");
}
return line;
}
private:
std::ifstream file_;
};
int clamp(int value, int low, int high) noexcept {
if (value < low) return low;
if (value > high) return high;
return value;
}
void load() {
try {
NotesFile notes{"notes.txt"};
std::cout << notes.read_line() << "\n";
} catch (const std::exception& error) {
std::cout << "error: " << error.what() << "\n";
}
}Common mistakes
- Catching every exception and doing nothing hides real failures and makes the program harder to debug.
- Using exceptions for ordinary control flow such as a missing value is slow and unclear.
- Writing a function that modifies an object and then throws leaves the object half-updated unless you plan for rollback.
- Marking a function noexcept and then letting an exception escape it calls std::terminate and ends the program.
Guard a resource with RAII
Write a class SafeFile that opens an std::ifstream in its constructor, throws std::runtime_error when the file does not open, and lets the destructor close the file automatically.
class SafeFile {
public:
};What does the strong exception guarantee promise?
Make it yours.
Keep a private note for this lesson, then turn the key ideas into flashcards you can revisit.
Lock in what you just learned.
Mark this lesson complete to save your place and move one step closer to the certificate.