Skip to main content

错误处理

·608 words·3 mins· loading
Raven005
Author
Raven005
A little bit about you
Table of Contents

错误处理方法概览
#

返回错误码 (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;
}
  • 优点

    • 类型安全,明确的错误语义
    • 可定制化,适应项目需求
    • 跨平台,无需特殊编译器支持
  • 缺点

    • 模板代码可能增加编译时间