1. 使用返回值(错误码)
适用于简单函数,尤其是性能敏感的场景。
// 返回 0 表示成功,非 0 表示错误码 int computesomething(int input, double& output) { if (input < 0) { return 1; // 错误码 1:非法输入 } output = sqrt(input); return 0; // 成功 } // 调用示例: double result; int err = computesomething(-1, result); if (err != 0) { std::cerr << "error: " << err << std::endl; // 处理错误... }
优点:
- 简单直接,性能高(无异常开销)。
- 适用于嵌入式或高性能计算。
缺点:
- 调用者必须检查返回值,容易遗漏。
- 错误信息不够丰富(只能返回数字代码)。
2. 使用 std::optional 或 std::expected(c++23)
适用于可能失败但不需要详细错误信息的函数。
#include <optional> #include <cmath> std::optional<double> safesqrt(double x) { if (x < 0) { return std::nullopt; // 表示失败 } return sqrt(x); } // 调用示例: auto result = safesqrt(-1); if (!result) { std::cerr << "error: invalid input" << std::endl; // 处理错误... }
优点:
- 比错误码更清晰,避免魔数(magic numbers)。
- 适用于可能失败但不需要详细错误信息的场景。
缺点:
无法携带详细的错误信息(只能表示成功/失败)。
3. 抛出异常(throw)
适用于严重错误或不可恢复的错误。
#include <stdexcept> double computesomething(int input) { if (input < 0) { throw std::invalid_argument("input must be non-negative"); } return sqrt(input); } // 调用示例: try { double result = computesomething(-1); } catch (const std::exception& e) { std::cerr << "error: " << e.what() << std::endl; // 处理错误... }
优点:
- 错误信息丰富(可以携带字符串描述)。
- 适用于严重错误,避免错误传播。
缺点:
- 异常处理有额外开销。
- 滥用异常可能导致代码难以维护。
4. 使用自定义错误结构(结构化错误)
适用于需要返回复杂错误信息的场景。
#include <string> #include <variant> // 定义可能的错误类型 struct error { int code; std::string message; }; // 使用 std::variant 表示可能成功或失败 template <typename t> using result = std::variant<t, error>; result<double> safedivide(double a, double b) { if (b == 0) { return error{1, "division by zero"}; } return a / b; } // 调用示例: auto result = safedivide(1, 0); if (std::holds_alternative<error>(result)) { auto err = std::get<error>(result); std::cerr << "error " << err.code << ": " << err.message << std::endl; // 处理错误... }
优点:
- 错误信息结构化(错误码 + 描述)。
- 适用于需要详细错误信息的 api。
缺点:
- 需要 c++17(std::variant)。
- 比错误码稍复杂。
5. 使用日志记录(logging)
适用于调试或非关键错误。
#include <iostream> bool computesomething(int input, double& output) { if (input < 0) { std::cerr << "[error] input must be non-negative" << std::endl; return false; } output = sqrt(input); return true; } // 调用示例: double result; if (!computesomething(-1, result)) { // 错误已打印,无需额外处理 }
优点:
简单直接,适合调试。
缺点:
错误处理不够灵活(日志可能被忽略)。
最佳实践建议
- 简单错误 → 使用 std::optional 或错误码。
- 关键错误 → 使用异常(如文件无法打开、内存不足)。
- 复杂错误 → 使用 std::expected(c++23)或自定义 result 类型。
- 调试/日志 → 结合日志记录(如 spdlog 或 std::cerr)。
你的选择应该基于:
- 性能需求(异常 vs 错误码)。
- 错误严重程度(可恢复 vs 不可恢复)。
- 代码可读性(结构化错误 vs 简单错误码)。
到此这篇关于c/c++中函数分装时错误处理的常见方法的文章就介绍到这了,更多相关c/c++错误处理内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论