在 C++ 中,异常处理是一种重要的错误管理机制,用于捕获程序运行时出现的问题并优雅地进行处理。相比传统的错误返回码,异常提供了结构化、清晰的处理方式,使代码逻辑更清晰、可维护性更强。
本文将深入剖析 C++ 异常处理的设计哲学、语法细节、异常安全性级别、以及在实际工程中的应用建议。
传统错误处理通常依赖返回值判断:
cpp复制编辑intdivide(int a, int b) {
if (b == 0) return-1; // 错误码return a / b;
}
这种方法的缺点包括:
而异常机制将错误检测和错误处理分离,极大提升代码整洁度和安全性。
cpp复制编辑try {
// 可能抛出异常的代码
} catch (const std::exception& e) {
// 异常处理逻辑
}
cpp复制编辑throw std::runtime_error("Something went wrong");
可以抛出任意类型的对象(但建议使用继承自 std::exception
的类型)。
cpp复制编辑try {
// ...
} catch (const std::invalid_argument& e) {
// 特定处理
} catch (const std::exception& e) {
// 通用处理
} catch (...) {
// 捕获所有异常
}
C++ 标准库中提供了一组通用异常类:
cpp复制编辑#include<stdexcept>
异常类型 | 描述 |
---|---|
std::exception | 所有标准异常的基类 |
std::runtime_error | 运行时错误(如溢出) |
std::logic_error | 逻辑错误(程序员失误) |
std::out_of_range | 容器越界访问 |
std::invalid_argument | 参数无效 |
std::length_error | 容器过长引发错误 |
标准异常类都实现了 what()
方法用于获取错误信息:
cpp复制编辑catch (const std::exception& e) {
std::cerr << e.what() << std::endl;
}
可以根据项目需求自定义异常类型,继承 std::exception
:
cpp复制编辑classMyException : public std::exception {
public:
constchar* what()constnoexceptoverride {
return"自定义异常发生";
}
};
也可以传递动态信息:
cpp复制编辑classDetailedException : public std::runtime_error {
public:
DetailedException(const std::string& msg)
: std::runtime_error(msg) {}
};
当异常发生时,C++ 会执行以下流程:
catch
捕获这意味着即使发生异常,也能确保局部对象正确释放资源。
例子:
cpp复制编辑classGuard {
public:
Guard() { std::cout << "Init\n"; }
~Guard() { std::cout << "Cleanup\n"; }
};
voidtest() {
Guard g;
throw std::runtime_error("error");
}
输出:
sql复制编辑Init
Cleanup
terminate called after ...
C++ 中函数按异常安全性可分为四个级别:
函数可能抛出异常,且对象状态不可预测,容易造成资源泄漏。
即使发生异常,程序保持有效状态,不泄漏资源。
操作失败时,原始对象保持完全不变(事务式)。
函数保证永远不会抛出异常(如 swap()
、析构函数)。
cpp复制编辑voidsafe_swap(std::vector<int>& a, std::vector<int>& b) noexcept {
a.swap(b);
}
建议所有析构函数和移动操作声明为 noexcept
。
RAII(Resource Acquisition Is Initialization)天然适配异常机制:
cpp复制编辑classFile {
public:
File(const std::string& path) {
handle = fopen(path.c_str(), "r");
if (!handle) throw std::runtime_error("打开失败");
}
~File() {
if (handle) fclose(handle);
}
private:
FILE* handle;
};
即使抛出异常,File
析构也会自动关闭文件。
构造函数中抛出的异常将导致对象创建失败,未完成的成员会被自动销毁。
cpp复制编辑classA {
public:
A() {
throw std::runtime_error("构造失败");
}
};
析构函数应为 noexcept
,否则若在栈展开过程中再次抛异常,程序将直接调用 std::terminate()
终止运行。
cpp复制编辑std::exception_ptr eptr;
voidworker() {
try {
throw std::runtime_error("线程异常");
} catch (...) {
eptr = std::current_exception();
}
}
主线程可用 std::rethrow_exception(eptr)
重新抛出。
catch
使用 const std::exception&
捕获所有标准异常noexcept
try/catch
中手动 delete
int
)cpp复制编辑voidfoo()noexcept;
适用于:
swap()
)cpp复制编辑template<typename T>
voidsafe_swap(T& a, T& b)noexcept(noexcept(a.swap(b))) {
a.swap(b);
}
根据实际类型决定是否抛异常,提升泛型代码健壮性。
C++ 的异常处理机制功能强大,但也充满陷阱。理解异常的传播机制、RAII 与异常的协作、异常安全级别等核心概念,是编写健壮、高质量 C++ 代码的前提。
关键要点总结:
文章评论(0条评论)
登录后参与讨论