Signal and handle errors with throw, try, and catch. Write exception-safe code using RAII, mark non-throwing functions noexcept, and return meaningful exit codes.
An exception unwinds the stack until a matching catch handles it — separating the error path from the happy path. Throw and catch exceptions derived from std::exception, and catch by const reference to avoid slicing. An unhandled exception calls std::terminate.
#include <iostream>
#include <stdexcept>
double divide(double a, double b) {
if (b == 0) throw std::invalid_argument("divide by zero");
return a / b;
}
int main() {
try {
std::cout << divide(10, 2) << "\n"; // 5
std::cout << divide(1, 0) << "\n"; // throws
} catch (const std::exception& e) { // catch by const reference
std::cout << "error: " << e.what() << "\n";
}
return 0;
}When an exception unwinds the stack, every local object’s destructor still runs — so any resource held by a smart pointer, lock, or file wrapper is released automatically. That is why RAII, not try/finally (which C++ doesn’t have), is how C++ guarantees cleanup on errors.
#include <memory>
#include <stdexcept>
struct Resource {
Resource() { /* acquire */ }
~Resource() { /* release — runs even if we throw below */ }
};
void process(bool fail) {
auto r = std::make_unique<Resource>(); // owned locally
if (fail)
throw std::runtime_error("boom"); // ~Resource() STILL runs
// ... normal work ...
} // ~Resource() runs here tooDerive your own exception types from std::exception for domain errors. Mark functions that cannot throw noexcept — it enables optimizations and signals intent (destructors and move operations should be noexcept). main’s return value is the process exit code the OS sees.
#include <exception>
#include <string>
class ConfigError : public std::exception {
std::string msg_;
public:
explicit ConfigError(std::string m) : msg_(std::move(m)) {}
const char* what() const noexcept override { return msg_.c_str(); }
};
int add(int a, int b) noexcept { return a + b; } // promises not to throw
int main() {
try {
throw ConfigError("missing port");
} catch (const std::exception& e) {
return 1; // non-zero exit code = failure
}
return 0;
}