基础语法
void mightFail() {
throw std::runtime_error("something went wrong");
}
try {
mightFail();
} catch (const std::runtime_error &e) {
std::cerr << e.what() << '\n';
} catch (const std::exception &e) {
std::cerr << "std exception: " << e.what() << '\n';
} catch (...) {
std::cerr << "unknown exception\n";
}
标准异常层次
std::exception
├── std::logic_error # 编程逻辑错误
│ ├── std::invalid_argument
│ ├── std::out_of_range
│ └── std::length_error
└── std::runtime_error # 运行时错误
├── std::range_error
├── std::overflow_error
└── std::system_error
// 抛合适的异常
if (index >= vec.size())
throw std::out_of_range("index out of range");
if (ptr == nullptr)
throw std::invalid_argument("null pointer");
// 自定义异常
class DatabaseError : public std::runtime_error {
public:
explicit DatabaseError(const std::string &msg)
: std::runtime_error("DB: " + msg) {}
};
noexcept — 不抛异常标记
void noFail() noexcept { /* 保证不抛异常 */ }
// 移动构造/赋值必须 noexcept(vector 扩容检测此标记)
class MyClass {
public:
MyClass(MyClass &&other) noexcept : data(other.data) {
other.data = nullptr;
}
};
// 条件 noexcept
template <typename T>
void swap(T &a, T &b) noexcept(noexcept(a = std::move(b))) {
T tmp = std::move(a);
a = std::move(b);
b = std::move(tmp);
}
// 编译期检查
static_assert(noexcept(noFail()));
RAII 与异常安全
// ❌ 不安全 — 异常导致资源泄漏
void unsafe() {
auto *f = fopen("data.txt", "r"); // 打开文件
processFile(f); // 可能抛异常
fclose(f); // 永远执行不到 → 泄漏
}
// ✅ RAII — 异常安全
void safe() {
std::ifstream f("data.txt"); // RAII 包装
processFile(f);
} // f 析构 → 自动调用 close,异常也保证执行
// ✅ 智能指针同理
void safePtr() {
auto p = std::make_unique<Resource>();
mightFail(); // 抛异常 → p 自动 delete
}
异常安全三等级
| 等级 | 语义 | 例子 |
|---|
| 基本 | 不泄漏,对象状态有效(可能改过) | 大多数 STL 操作 |
| 强 | 失败回滚到调用前状态 | vector::push_back 失败 → 不变 |
| 不抛 | 绝不抛异常 | swap、析构函数 |
// 强异常安全模式:copy-and-swap
class MyVec {
void push_back(const T &val) {
auto tmp = m_data; // 先拷贝
tmp.push_back(val); // 在新副本上操作
m_data.swap(tmp); // 交换(不抛异常)
} // 如果 push_back 抛异常,m_data 未变
};
最佳实践
// ✅ 按 const 引用捕获
catch (const std::exception &e)
// ✅ 析构函数绝不抛异常(默认 noexcept)
~MyClass() /* noexcept */ { /* ... */ }
// ✅ 构造函数抛异常前释放已分配资源
// 用 RAII 成员变量 → 自动处理
// ❌ 不要在析构函数中抛异常
~BadClass() {
throw std::runtime_error("..."); // 💥 std::terminate
}
// ✅ 如果析构中可能失败,吞掉或记录
~FileWriter() {
try { flush(); }
catch (...) { /* 记录日志,不重抛 */ }
}
何时用异常
| 用异常 | 不用异常 |
|---|
| 构造函数失败 | 性能热点(noexcept 保证) |
| 深层调用栈错误传播 | 预期中的失败(std::optional) |
| 罕见的、不可恢复的错误 | 嵌入式 / -fno-exceptions 环境 |