C++: Exception Handling
Last updated: 2026-08-26
When programs run, unexpected things always happen: files don't exist, memory runs out, network disconnects...
If left unhandled, the program crashes.
Exception handling is the program's "airbag" — when problems occur, it responds gracefully instead of crashing outright.
1. Exception Handling Basics
(1) 1.1 What Is an Exception?
An exception is an error or unexpected condition that occurs during program execution.
Common Exceptions:
- Division by zero
- Accessing a null pointer
- File open failure
- Memory allocation failure
(2) 1.2 The Three Elements of Exception Handling
C++ exception handling uses three keywords:
| Keyword | Function |
|---|---|
try |
Monitor code that may throw errors |
catch |
Catch and handle exceptions |
throw |
Throw an exception |
(3) 1.3 Basic Example
Example: Division by Zero Exception (Difficulty ⭐)
▶ Example 1: Code Example (Difficulty ⭐)
#include <iostream>
int divide(int a, int b) {
if (b == 0) {
throw "Divisor cannot be 0!"; // Throw exception
}
return a / b;
}
int main() {
try {
std::cout << divide(10, 2) << std::endl; // Normal
std::cout << divide(10, 0) << std::endl; // Throws exception
}
catch (const char* msg) { // Catch exception
std::cerr << "Error: " << msg << std::endl;
}
std::cout << "Program continues running" << std::endl;
return 0;
}
Output:
5
Error: Divisor cannot be 0!
Program continues running
Execution Result:
5
Error: Divisor cannot be 0!
Program continues running
💡 Tip:
- After an exception is caught, the program does not crash — it continues executing
2. Exception Types
(1) 2.1 Types That Can Be Thrown
throw can throw any type:
| Type | Example |
|---|---|
| Basic types | throw 42; |
| Strings | throw "error"; |
| Standard exceptions | throw std::runtime_error("error"); |
| Custom exceptions | throw MyException(); |
(2) 2.2 Catching Multiple Exceptions
Example: Catching Different Types of Exceptions (Difficulty ⭐⭐)
#include <iostream>
### ▶ Example 2: Code Example (Difficulty ⭐)
#include <string>
void process(int value) {
if (value == 0) {
throw 0; // Throw int
}
if (value < 0) {
throw std::string("Negative number"); // Throw string
}
}
int main() {
try {
process(-5);
}
catch (int e) {
std::cerr << "Caught int exception: " << e << std::endl;
}
catch (const std::string& e) {
std::cerr << "Caught string exception: " << e << std::endl;
}
return 0;
}
3. Standard Exceptions
(1) 3.1 std::exception Hierarchy
The C++ standard library defines a set of exception classes, all in stdexcept:
std::exception
├── std::logic_error
│ ├── std::invalid_argument
│ ├── std::out_of_range
│ └── std::length_error
└── std::runtime_error
├── std::overflow_error
└── std::underflow_error
(2) 3.2 Common Standard Exceptions
| Exception Class | Description |
|---|---|
std::invalid_argument |
Invalid argument |
std::out_of_range |
Out of bounds |
std::runtime_error |
Runtime error |
std::bad_alloc |
Memory allocation failure (new failed) |
Example: Using Standard Exceptions (Difficulty ⭐⭐)
#include <iostream>
#include <stdexcept>
#include <vector>
int main() {
std::vector<int> v = {1, 2, 3};
try {
v.at(10); // Out-of-bounds access
}
catch (const std::out_of_range& e) {
std::cerr << "Caught exception: " << e.what() << std::endl;
}
return 0;
}
4. Custom Exceptions
(1) 4.1 Inheriting from std::exception
Best Practice: Custom exception classes should inherit from std::exception.
Example: Custom Exception Class (Difficulty ⭐⭐⭐)
#include <iostream>
#include <exception>
#include <string>
// Custom exception class
class MyException : public std::exception {
private:
std::string msg;
public:
MyException(const std::string& msg) : msg(msg) {}
const char* what() const noexcept override {
return msg.c_str();
}
};
int main() {
try {
throw MyException("Custom exception");
}
catch (const MyException& e) {
std::cerr << "Caught custom exception: " << e.what() << std::endl;
}
return 0;
}
5. Exception Safety
(1) 5.1 What Is Exception Safety?
Exception safety means that even when an exception is thrown, the program maintains a consistent state (no resource leaks, no data corruption).
Three guarantee levels:
- Basic guarantee: The program state is consistent, but may have changed
- Strong guarantee: The operation either succeeds or is completely rolled back (as if it never happened)
- No-throw guarantee: The function never throws an exception
(2) 5.2 RAII — A Powerful Tool for Resource Management
RAII (Resource Acquisition Is Initialization): Manage resources through objects, with destructors automatically releasing them.
Example: Using Smart Pointers for Exception Safety (Difficulty ⭐⭐⭐)
#include <iostream>
#include <memory>
void process() {
// Use unique_ptr to manage memory; even if an exception is thrown, it will be automatically freed
std::unique_ptr<int> p(new int(42));
// ... code that may throw exceptions ...
std::cout << *p << std::endl;
} // p is automatically freed
int main() {
try {
process();
}
catch (...) {
std::cerr << "Caught exception" << std::endl;
}
return 0;
}
6. The noexcept Keyword
(1) 6.1 Basic Usage
C++11 introduced noexcept, which declares that a function does not throw exceptions.
Example: Declaring No-Throw (Difficulty ⭐)
void safeFunction() noexcept {
// This function guarantees not to throw exceptions
}
void unsafeFunction() {
// May throw exceptions
}
💡 Tip:
- Destructors are
noexceptby default noexcepthelps the compiler optimize
❓ FAQ
Q: When should I use exceptions? A: For truly unexpected situations, not for normal control flow.
Q: What is catch(...)? A: It catches all exceptions, typically used to clean up resources before re-throwing.
try {
// ...
}
catch (...) {
// Clean up resources
throw; // Re-throw
}
Q: Can constructors throw exceptions? A: Yes, but be careful about memory leaks. Use smart pointers or RAII to manage resources.
▶ Example 3: Catching Exceptions (Difficulty ⭐)
#include <iostream>
#include <stdexcept>
double divide(double a, double b) {
if (b == 0) {
throw std::runtime_error("Divisor cannot be 0");
}
return a / b;
}
int main() {
try {
std::cout << divide(10, 2) << std::endl;
std::cout << divide(10, 0) << std::endl;
} catch (const std::exception& e) {
std::cout << "Error: " << e.what() << std::endl;
}
return 0;
}
Output:
Error: Divisor cannot be 0
try-catch catches exceptions, throw throws exceptions. what() returns the error message.
| Concept | Key Point |
|---|---|
| try-catch-throw | The three elements of exception handling |
| Standard exceptions | std::runtime_error, etc. |
| Custom exceptions | Inherit from std::exception |
| Exception safety | RAII ensures no resource leaks |
| noexcept | Declares that a function does not throw |
📖 Summary
- try/catch: Catch and handle exceptions
- throw: Throw exceptions
- std::exception: Standard exception base class
- Custom exceptions: Implement your own exception classes by inheriting from
std::exception
📝 Exercises
-
Basic (Difficulty ⭐): Write a division function that throws
std::runtime_errorwhen the divisor is 0, and usetry/catchinmainto catch and output the error message. -
Intermediate (Difficulty ⭐⭐): Define a custom exception class (inheriting from
std::exception) with two fields: error code and error description. After throwing it, retrieve both in the catch block. -
Challenge (Difficulty ⭐⭐⭐): Implement a "resource guard" class that acquires resources in the constructor and releases them in the destructor. Even if an exception is thrown in between, resources are correctly released (RAII principle).
- Three parts of exception handling: try monitors, throw throws, catch catches
- Standard exception classes inherit from std::exception
- catch matches by type; multiple catch blocks are checked top to bottom
- Stack unwinding: after an exception is thrown, catch is searched layer by layer
- noexcept declares that a function does not throw
Next Lesson: Advanced File Operations (#41)