C++ 错误处理简介
错误处理是开发可靠 C++ 软件的关键任务之一。与某些语言(例如 Python 或 Java)不同,异常是唯一的标准机制,而 C++ 为开发者提供了多种方法。正确策略的选择决定了代码的稳定性、性能和可读性。在本文中,我们将分析经典的 try/catch 结构、C++17/20 标准的现代特性,以及替代的错误处理方法。
1. 经典方法:try/catch 结构
C++ 中异常处理的主要机制基于三个关键字:try、catch 和 throw。异常是任何类型的对象,在发生错误时被“抛出”,并在相应的块中被“捕获”。
#include <iostream>#include <stdexcept>
double divide(double a, double b) { if (b == 0) { throw std::runtime_error("除以零!"); } return a / b;}
int main() { try { double result = divide(10, 0); std::cout << "结果: " << result << std::endl; } catch (const std::runtime_error& e) { std::cerr << "错误: " << e.what() << std::endl; } catch (...) { std::cerr << "未知错误!" << std::endl; } return 0;}重要事项:
- 异常层次结构:建议抛出继承自
std::exception的类对象(例如std::runtime_error、std::logic_error)。 - 通过引用捕获:始终通过常量引用捕获异常(
const std::exception&),以避免切片和不必要的复制。 - 通用处理程序:
catch (...)捕获任何异常,但不提供对错误对象的访问。仅将其用作最后的手段。
2. 替代方法:返回码和 std::optional
异常并不总是最佳选择。在高负载系统或嵌入式编程中,通常更倾向于更轻量的机制。
2.1. 返回码 (error codes)
函数返回一个整数或枚举,指示成功或错误类型。这种方法简单高效,但需要显式检查结果。
enum class ErrorCode { Success, DivisionByZero, Overflow };
ErrorCode safe_divide(double a, double b, double& result) { if (b == 0.0) return ErrorCode::DivisionByZero; result = a / b; return ErrorCode::Success;}
int main() { double res; ErrorCode err = safe_divide(10, 0, res); if (err != ErrorCode::Success) { // 错误处理 }}2.2. std::optional (C++17)
一个可以包含值或为空的容器。非常适合错误意味着没有结果的情况。
#include <optional>
std::optional<double> divide_opt(double a, double b) { if (b == 0.0) return std::nullopt; return a / b;}
int main() { auto result = divide_opt(10, 2); if (result) { std::cout << *result << std::endl; } else { std::cout << "除法错误" << std::endl; }}3. 现代特性:noexcept 和 std::expected (C++23)
C++ 标准不断发展,为错误处理提供了更具表现力的工具。
3.1. noexcept 规范
关键字 noexcept 指示函数不会抛出异常。这有助于编译器优化代码,并使接口更加清晰。
void l