在 C++ 的错误处理体系中,异常类是核心。标准库提供了基类 std::exception,但在实际项目中往往需要根据业务逻辑定义更细粒度的异常。C++20 通过引入 std::source_location、[[nodiscard]] 等特性,为自定义异常提供了更多便利。本文将演示:
- 定义一个可携带错误码、错误消息和源代码位置信息的异常类
- 使用
std::exception_ptr捕获并重抛异常 - 在
try–catch块中优雅地打印异常信息
1. 设计异常类
#include <exception>
#include <string>
#include <source_location>
#include <iostream>
#include <format>
class MyException : public std::exception {
public:
enum class Code {
INVALID_ARGUMENT,
OUT_OF_RANGE,
UNKNOWN
};
MyException(Code code,
std::string message = {},
std::source_location loc = std::source_location::current())
: code_(code),
message_(std::move(message)),
file_(loc.file_name()),
line_(loc.line()),
col_(loc.column()) {}
// 重写 what(),返回完整错误描述
[[nodiscard]] const char* what() const noexcept override {
if (what_msg_.empty()) {
what_msg_ = std::format(
"MyException: {} (code: {}), at {}:{}:{}",
message_,
static_cast <int>(code_),
file_, line_, col_
);
}
return what_msg_.c_str();
}
Code code() const noexcept { return code_; }
private:
Code code_;
std::string message_;
std::string file_;
int line_;
int col_;
mutable std::string what_msg_;
};
说明
std::source_location自动捕获抛出点的文件名、行号和列号,方便定位。[[nodiscard]]用于标记what()的返回值不可忽略,避免遗漏错误信息。std::format(C++20)用于构造错误字符串,简洁且安全。
2. 抛出异常并使用 std::exception_ptr
void risky_operation(int value) {
if (value < 0) {
throw MyException(MyException::Code::INVALID_ARGUMENT,
"value must be non‑negative");
} else if (value > 100) {
throw MyException(MyException::Code::OUT_OF_RANGE,
"value exceeds maximum allowed");
}
std::cout << "Operation succeeded with value " << value << '\n';
}
在调用代码中:
#include <exception>
int main() {
try {
risky_operation(-5);
} catch (...) {
// 捕获所有异常,保留异常指针
std::exception_ptr eptr = std::current_exception();
std::cout << "Caught an exception. Re‑throwing to outer handler.\n";
try {
if (eptr) std::rethrow_exception(eptr);
} catch (const MyException& e) {
std::cerr << "Handled MyException: " << e.what() << '\n';
} catch (const std::exception& e) {
std::cerr << "Handled std::exception: " << e.what() << '\n';
} catch (...) {
std::cerr << "Unhandled unknown exception.\n";
}
}
}
要点
std::current_exception()捕获当前异常并返回std::exception_ptr。std::rethrow_exception()可在不同作用域重抛,保持异常栈完整。- 通过多重
catch可以分别处理自定义异常和标准异常,确保错误信息完整可读。
3. 运行结果示例
Caught an exception. Re‑throwing to outer handler.
Handled MyException: MyException: value must be non‑negative (code: 0), at /path/to/file.cpp:78:5
4. 小结
- 自定义异常类:继承
std::exception,添加业务字段、错误码和源代码位置。 std::source_location:自动收集抛出点信息,简化错误追踪。std::exception_ptr+rethrow_exception:实现跨作用域异常传递,保持错误栈完整。- C++20:利用
std::format和属性[[nodiscard]]提升代码可读性与安全性。
通过上述模式,你可以在 C++20 项目中构建一个健壮、可追踪且易于维护的异常处理体系。