输入关键词开始搜索

C++ 异常处理

基础语法

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 环境