错误处理方法概览#
返回错误码 (C style)#
// 传统 C 风格
int open_file(const char* path, FILE** out) {
FILE* f = fopen(path, "r");
if (!f) {
return -1; // 返回错误码
}
*out = f;
return 0; // 成功返回 0
}
// 调用方
FILE* file;
int ret = open_file("data.txt", &file);
if (ret != 0) {
printf("打开文件失败: %d\n", ret);
return;
}
// 使用 file...
优点
- 简单直接,性能开销为零
- 无运行时依赖,适合嵌入式系统
- 与 C 代码兼容性好
缺点
- 输出参数必须在堆上分配或使用指针
- 错误容易被忽略(忘记检查返回值)
- 无法与函数返回值结合使用
- 错误信息有限(通常只是一个整数)
异常机制#
// 现代 C++ 异常
std::string read_file(const std::string& path) {
std::ifstream file(path);
if (!file.is_open()) {
throw std::runtime_error("无法打开文件: " + path);
}
std::string content;
std::string line;
while (std::getline(file, line)) {
content += line + "\n";
}
return content;
}
// 调用方
try {
std::string content = read_file("data.txt");
std::cout << content << std::endl;
} catch (const std::runtime_error& e) {
std::cerr << "错误: " << e.what() << std::endl;
} catch (...) {
std::cerr << "未知错误" << std::endl;
}优点
- 类型安全(异常类型携带信息)
- 强制处理(未捕获的异常会终止程序)
- 分离正常逻辑和错误处理
- 支持异常安全的 RAII
缺点
- 运行时开销(栈展开、异常表)
- 控制流不透明(难以追踪执行路径)
- 资源管理需要仔细设计(异常安全)
- 跨语言边界困难(C、其他语言)
- 可能导致性能不可预测
std::optional#
// std::optional - 表示可能有或没有值
#include <optional>
std::optional<int> parse_int(const std::string& str) {
try {
return std::stoi(str);
} catch (...) {
return std::nullopt; // 解析失败
}
}
// 调用方
auto result = parse_int("123");
if (result.has_value()) {
int value = result.value();
std::cout << "解析成功: " << value << std::endl;
} else {
std::cout << "解析失败" << std::endl;
}
// 使用 value_or 提供默认值
int value = parse_int("invalid").value_or(0);优点
- 类型安全,明确表达"可选"语义
- 无运行时开销(零成本抽象)
- 标准库支持,无需第三方库
- 支持函数式编程风格
缺点
- 只能表示"成功"或"无值",无法携带错误信息
- 错误原因不明确(为什么失败?)
- 需要显式检查 has_value()
std::excepted (C++23)#
// std::expected - 类似 Rust 的 Result
#include <expected>
enum class ParseError {
InvalidFormat,
OutOfRange
};
std::expected<int, ParseError> parse_int(const std::string& str) {
try {
return std::stoi(str);
} catch (const std::invalid_argument&) {
return std::unexpected(ParseError::InvalidFormat);
} catch (const std::out_of_range&) {
return std::unexpected(ParseError::OutOfRange);
}
}
// 调用方
auto result = parse_int("123");
if (result) {
std::cout << "解析成功: " << *result << std::endl;
} else {
ParseError error = result.error();
std::cout << "解析失败: " << static_cast<int>(error) << std::endl;
}优点
- 类型安全,携带成功值或错误信息
- 零成本抽象,无运行时开销
- 函数式编程支持(map, and_then 等)
- 标准库支持(C++23+)
缺点
- 库支持可能不完善(截至 2024 年)
- 错误类型设计需要仔细考虑
std::variant (c++17)#
// std::variant - 类型安全的联合体
#include <variant>
struct Success { int value; };
struct Error { std::string message; };
using Result = std::variant<Success, Error>;
Result divide(int a, int b) {
if (b == 0) {
return Error{"除零错误"};
}
return Success{a / b};
}
// 调用方
Result result = divide(10, 2);
if (std::holds_alternative<Success>(result)) {
Success s = std::get<Success>(result);
std::cout << "结果: " << s.value << std::endl;
} else {
Error e = std::get<Error>(result);
std::cout << "错误: " << e.message << std::endl;
}
// 使用 std::visit
std::visit([](auto&& arg) {
using T = std::decay_t<decltype(arg)>;
if constexpr (std::is_same_v<T, Success>) {
std::cout << "成功: " << arg.value << std::endl;
} else {
std::cout << "错误: " << arg.message << std::endl;
}
}, result);优点
- 类型安全,编译期检查
- 可携带任意类型的成功或错误信息
- 无开销抽象
缺点
- 语法较为繁琐(holds_alternative, get)
- 不够直观(variant 不是明确的"结果"类型)
自定义类型#
// 自定义 Result 模板(类似 Rust)
template<typename T, typename E>
class Result {
public:
static Result Ok(T value) {
return Result(std::move(value));
}
static Result Err(E error) {
return Result(std::move(error));
}
bool is_ok() const { return _state == State::Ok; }
bool is_err() const { return _state == State::Err; }
T& unwrap() {
assert(is_ok());
return _value;
}
E& unwrap_err() {
assert(is_err());
return _error;
}
// map, and_then, or_else...
private:
enum class State { Ok, Err };
State _state;
union {
T _value;
E _error;
};
};
// 使用示例
enum class DatabaseError {
ConnectionFailed,
QueryFailed
};
Result<User, DatabaseError> get_user(int uid) {
if (uid <= 0) {
return Result<User, DatabaseError>::Err(DatabaseError::QueryFailed);
}
return Result<User, DatabaseError>::Ok(User{uid, "Alice"});
}
auto result = get_user(123);
if (result.is_ok()) {
User user = result.unwrap();
std::cout << "用户: " << user.name << std::endl;
} else {
DatabaseError err = result.unwrap_err();
std::cout << "错误: " << static_cast<int>(err) << std::endl;
}优点
- 类型安全,明确的错误语义
- 可定制化,适应项目需求
- 跨平台,无需特殊编译器支持
缺点
- 模板代码可能增加编译时间

