当前位置: 首页 > news >正文

异常和自定义错误码使用时机

一、何时抛出异常

1. 应该抛出异常的典型场景

#include <stdexcept> #include <string> #include <vector> class NetworkLibrary { public: // ✅ 构造函数失败 - 对象无法正常使用 NetworkLibrary(const std::string& config) { if (!parseConfig(config)) { throw std::invalid_argument("Invalid network configuration format"); } } // ✅ 前置条件严重违反 - 无法继续执行 void sendData(const std::vector<char>& data) { if (data.empty()) { throw std::logic_error("Cannot send empty data buffer"); } if (!isConnected()) { throw std::runtime_error("Network connection not established"); } } // ✅ 资源获取失败 - 构造函数或初始化失败 void connect(const std::string& host, int port) { if (port <= 0 || port > 65535) { throw std::out_of_range("Invalid port number: " + std::to_string(port)); } // 尝试连接 if (!establishConnection(host, port)) { throw std::runtime_error("Failed to connect to " + host + ":" + std::to_string(port)); } } private: bool parseConfig(const std::string&) { return false; } bool isConnected() const { return false; } bool establishConnection(const std::string&, int) { return false; } };

2. 异常适用的关键判断标准

class FileProcessor { public: // ✅ 不可恢复的错误 - 应该抛异常 std::string readFile(const std::string& path) { std::ifstream file(path); if (!file.is_open()) { // 无法打开文件,程序无法继续正常执行 throw std::runtime_error("Cannot open file: " + path); } std::string content; if (!std::getline(file, content)) { // 读取失败,但文件可能损坏 throw std::runtime_error("File read failed: " + path); } return content; } // ✅ 违反契约/前置条件 - 应该抛异常 void writeData(const void* data, size_t size) { if (data == nullptr) { throw std::invalid_argument("writeData: data pointer cannot be null"); } if (size > MAX_FILE_SIZE) { throw std::length_error("Data size exceeds maximum file size"); } } // ✅ 无法恢复的内部错误 void process() { try { // 一些处理... if (internalStateCorrupted()) { // 内部状态损坏,无法继续可靠运行 throw std::logic_error("Internal state corrupted"); } } catch (...) { // 清理资源,然后重新抛出 cleanup(); throw; // 重新抛出,让调用者处理 } } private: static constexpr size_t MAX_FILE_SIZE = 1024 * 1024 * 100; // 100MB bool internalStateCorrupted() { return false; } void cleanup() {} };

二、何时使用自定义错误类型

自定义错误类型更适合预期内的错误场景,用户通常需要根据具体错误类型做出不同响应。

1. 枚举错误码方式

#include <system_error> #include <string> #include <iostream> namespace NetworkError { // 定义错误码枚举 enum class Code { Success = 0, ConnectionTimeout, ConnectionRefused, HostNotFound, NetworkUnreachable, AuthenticationFailed, SSLError, ProtocolError, Disconnected }; // 错误类别 - 提供错误描述 class NetworkErrorCategory : public std::error_category { public: const char* name() const noexcept override { return "network"; } std::string message(int ev) const override { switch (static_cast<Code>(ev)) { case Code::Success: return "Operation successful"; case Code::ConnectionTimeout: return "Connection timeout"; case Code::ConnectionRefused: return "Connection refused"; case Code::HostNotFound: return "Host not found"; case Code::NetworkUnreachable: return "Network unreachable"; case Code::AuthenticationFailed: return "Authentication failed"; case Code::SSLError: return "SSL/TLS error"; case Code::ProtocolError: return "Protocol error"; case Code::Disconnected: return "Connection disconnected"; default: return "Unknown network error"; } } }; // 获取全局错误类别实例 const std::error_category& network_category() { static NetworkErrorCategory instance; return instance; } // 创建 error_code inline std::error_code make_error_code(Code e) { return std::error_code(static_cast<int>(e), network_category()); } // 创建 error_condition(用于比较) inline std::error_condition make_error_condition(Code e) { return std::error_condition(static_cast<int>(e), network_category()); } } // 注册为 is_error_code_enum,允许自动转换 namespace std { template <> struct is_error_code_enum<NetworkError::Code> : true_type {}; } // 使用示例 class NetworkClient { public: // 返回错误码而不是抛出异常 std::error_code connect(const std::string& host, int port) { if (host.empty()) { return NetworkError::make_error_code(NetworkError::Code::HostNotFound); } // 模拟各种错误情况 if (host == "timeout.example.com") { return NetworkError::Code::ConnectionTimeout; } if (host == "refused.example.com") { return NetworkError::Code::ConnectionRefused; } if (host == "auth.example.com") { return NetworkError::Code::AuthenticationFailed; } // 成功连接 m_connected = true; return std::error_code(); // 默认构造为 success } // 发送数据,返回错误码 std::error_code send(const std::string& data) { if (!m_connected) { return NetworkError::Code::Disconnected; } // 模拟发送失败 if (data.length() > 1024) { return NetworkError::Code::ProtocolError; } return std::error_code(); // success } bool isConnected() const { return m_connected; } private: bool m_connected = false; }; // 使用示例 void useNetworkClient() { NetworkClient client; // 连接并检查错误 auto ec = client.connect("refused.example.com", 80); if (ec) { std::cerr << "Connection failed: " << ec.message() << std::endl; // 可以根据具体错误码采取不同策略 if (ec == NetworkError::Code::ConnectionTimeout) { std::cout << "Will retry after 5 seconds..." << std::endl; // 安排重试 } else if (ec == NetworkError::Code::HostNotFound) { std::cout << "Please check the hostname" << std::endl; } else if (ec == NetworkError::Code::AuthenticationFailed) { std::cout << "Please check credentials" << std::endl; } } // 发送数据 auto sendEc = client.send("Hello"); if (sendEc) { std::cerr << "Send failed: " << sendEc.message() << std::endl; } }

2. 轻量级状态码方式

#include <variant> #include <optional> #include <string> #include <iostream> namespace Database { // 预定义的错误码 enum class Error { OK = 0, NOT_FOUND, DUPLICATE_KEY, CONSTRAINT_VIOLATION, DEADLOCK, CONNECTION_LOST, TIMEOUT }; // 错误信息结构 struct ErrorInfo { Error code; std::string message; std::string query; // 出错的查询 int line; // 代码行号 explicit operator bool() const { return code != Error::OK; } }; // 结果类型:要么是 T,要么是 ErrorInfo template<typename T> using Result = std::variant<T, ErrorInfo>; // 查询结果 class QueryResult { public: static QueryResult success(std::vector<std::string> rows) { return QueryResult(std::move(rows)); } static QueryResult failure(Error code, std::string message, std::string query) { return QueryResult(code, std::move(message), std::move(query)); } bool isValid() const { return m_error.code == Error::OK; } const std::vector<std::string>& rows() const { if (!isValid()) { throw std::logic_error("Cannot get rows from failed query"); } return m_rows; } const ErrorInfo& error() const { return m_error; } private: QueryResult(std::vector<std::string> rows) : m_rows(std::move(rows)), m_error{Error::OK} {} QueryResult(Error code, std::string message, std::string query) : m_error{code, std::move(message), std::move(query)} {} std::vector<std::string> m_rows; ErrorInfo m_error; }; // 数据库连接类 class Connection { public: // 执行查询,返回 QueryResult 而不是抛出异常 QueryResult execute(const std::string& sql) { // 模拟各种错误情况 if (sql.find("SELECT") == 0) { if (sql.find("nonexistent") != std::string::npos) { return QueryResult::failure( Error::NOT_FOUND, "Table or view not found", sql ); } // 成功查询 return QueryResult::success({"row1", "row2"}); } if (sql.find("INSERT") == 0) { if (sql.find("duplicate") != std::string::npos) { return QueryResult::failure( Error::DUPLICATE_KEY, "Duplicate key violation", sql ); } return QueryResult::success({"Inserted 1 row"}); } return QueryResult::failure( Error::CONSTRAINT_VIOLATION, "Unknown SQL command", sql ); } // 或者使用 Result<T> 模式 Result<int> insert(const std::string& table, const std::string& data) { // 模拟插入 if (table == "users" && data.find("admin") != std::string::npos) { return ErrorInfo{ Error::DUPLICATE_KEY, "Username already exists", "INSERT INTO users ...", __LINE__ }; } return 1; // 返回插入的行数 } }; } // 使用示例 void useDatabase() { Database::Connection db; // 使用 QueryResult auto result = db.execute("SELECT * FROM nonexistent"); if (!result.isValid()) { const auto& err = result.error(); std::cerr << "Database error [" << static_cast<int>(err.code) << "]: " << err.message << std::endl; std::cerr << "Failed query: " << err.query << std::endl; // 根据错误码决定处理策略 switch (err.code) { case Database::Error::NOT_FOUND: std::cout << "Creating table..." << std::endl; // 创建表然后重试 break; case Database::Error::CONNECTION_LOST: std::cout << "Reconnecting..." << std::endl; // 重连 break; default: std::cout << "Cannot recover, aborting" << std::endl; break; } } else { for (const auto& row : result.rows()) { std::cout << "Row: " << row << std::endl; } } // 使用 Result<T> 模式 auto insertResult = db.insert("users", "admin"); if (std::holds_alternative<Database::ErrorInfo>(insertResult)) { auto err = std::get<Database::ErrorInfo>(insertResult); std::cerr << "Insert failed at line " << err.line << ": " << err.message << std::endl; } else { int rows = std::get<int>(insertResult); std::cout << "Inserted " << rows << " rows" << std::endl; } }

3. 回调方式

#include <functional> #include <string> #include <chrono> namespace AsyncNetwork { // 错误类型 struct Error { enum Type { None, Timeout, ConnectionRefused, DNSFailure, SSLError, Disconnected } type = None; std::string message; int systemErrorCode = 0; explicit operator bool() const { return type != None; } }; // 数据回调类型 using DataCallback = std::function<void(const char* data, size_t size)>; using ErrorCallback = std::function<void(const Error& error)>; using CompletionCallback = std::function<void(bool success, const Error& error)>; class AsyncHttpClient { public: // 异步请求,通过回调通知结果 void get(const std::string& url, DataCallback onData, CompletionCallback onComplete, ErrorCallback onError) { // 模拟异步操作 std::thread([=]() { // 模拟 DNS 解析 if (url.find("invalid") != std::string::npos) { Error err; err.type = Error::DNSFailure; err.message = "Could not resolve hostname"; onError(err); onComplete(false, err); return; } // 模拟连接 if (url.find("timeout") != std::string::npos) { std::this_thread::sleep_for(std::chrono::seconds(5)); Error err; err.type = Error::Timeout; err.message = "Connection timeout"; onError(err); onComplete(false, err); return; } // 模拟数据传输 std::string response = "HTTP/1.1 200 OK\r\n\r\nHello World"; onData(response.c_str(), response.size()); // 完成 onComplete(true, Error{}); }).detach(); } // 简化的版本,只传一个回调 void post(const std::string& url, const std::string& data, std::function<void(const std::string& response, const Error& error)> callback) { std::thread([=]() { // 模拟工作 std::this_thread::sleep_for(std::chrono::milliseconds(100)); if (url.find("error") != std::string::npos) { Error err; err.type = Error::ConnectionRefused; err.message = "Connection refused by server"; callback("", err); } else { callback("Post successful", Error{}); } }).detach(); } }; } // 使用示例 void useAsyncClient() { AsyncNetwork::AsyncHttpClient client; std::cout << "Starting async request..." << std::endl; client.get("https://api.example.com/data", // 数据回调 [](const char* data, size_t size) { std::cout << "Received " << size << " bytes: " << std::string(data, size) << std::endl; }, // 完成回调 [](bool success, const AsyncNetwork::Error& error) { if (success) { std::cout << "Request completed successfully" << std::endl; } else { std::cout << "Request failed: " << error.message << std::endl; } }, // 错误回调 [](const AsyncNetwork::Error& error) { std::cerr << "Error occurred: " << error.message << std::endl; } ); // 简化的 post 请求 client.post("https://api.example.com/upload", "Hello Server", [](const std::string& response, const AsyncNetwork::Error& error) { if (error) { std::cerr << "Upload failed: " << error.message << std::endl; } else { std::cout << "Upload success: " << response << std::endl; } } ); std::cout << "Requests are running in background..." << std::endl; std::this_thread::sleep_for(std::chrono::seconds(2)); }

三、决策指南

1. 选择异常的场景

// ✅ 选择异常 class MathLibrary { public: double divide(double a, double b) { if (std::abs(b) < 1e-10) { // 除以零是编程错误,应该抛出异常 throw std::domain_error("Division by zero"); } return a / b; } std::vector<int> parseToInts(const std::string& input) { std::vector<int> result; try { // 可能抛出异常的字符串处理 size_t pos = 0; while (pos < input.length()) { size_t next; int value = std::stoi(input.substr(pos), &next); result.push_back(value); pos += next; } } catch (const std::invalid_argument& e) { // 输入格式错误,无法继续解析 throw std::runtime_error("Invalid number format in input"); } return result; } };

2. 选择错误码/自定义类型的场景

// ✅ 选择错误码 class FileSystem { public: // 文件可能存在也可能不存在,这是预期的情况 std::pair<std::string, bool> readFileIfExists(const std::string& path) { std::ifstream file(path); if (!file) { return {"", false}; // 返回bool表示是否存在 } std::string content((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>()); return {content, true}; } // 预期内可能出现的多种错误 enum class WriteResult { Success, PermissionDenied, DiskFull, InvalidPath, FileLocked }; WriteResult writeFile(const std::string& path, const std::string& content) { // 检查路径 if (path.empty() || path.find("..") != std::string::npos) { return WriteResult::InvalidPath; } // 模拟磁盘满 if (content.size() > 1024 * 1024) { // 1MB return WriteResult::DiskFull; } std::ofstream file(path); if (!file) { return WriteResult::PermissionDenied; } file << content; return WriteResult::Success; } }; // 使用示例 void useFileSystem() { FileSystem fs; auto [content, exists] = fs.readFileIfExists("config.txt"); if (exists) { std::cout << "Config: " << content << std::endl; } else { std::cout << "Config file not found, using defaults" << std::endl; } auto result = fs.writeFile("/etc/config", "settings"); switch (result) { case FileSystem::WriteResult::Success: std::cout << "Saved successfully" << std::endl; break; case FileSystem::WriteResult::PermissionDenied: std::cerr << "Need administrator privileges" << std::endl; break; case FileSystem::WriteResult::DiskFull: std::cerr << "No space left on device" << std::endl; break; default: std::cerr << "Unknown error" << std::endl; break; } }

3. 混合策略

class RobustNetworkLibrary { public: // 关键函数:抛出异常表示严重错误 void initialize(const std::string& config) { if (!loadConfiguration(config)) { throw std::runtime_error("Failed to load configuration"); } if (!initializeSSL()) { throw std::runtime_error("SSL initialization failed"); } } // 普通函数:返回错误码表示可恢复的错误 std::error_code connect(const std::string& host, int port) { if (!m_initialized) { return make_error_code(ErrorCode::NotInitialized); } auto ec = performConnection(host, port); if (!ec) { m_connected = true; } return ec; } // 异步操作:使用回调处理错误 void downloadAsync(const std::string& url, std::function<void(const std::string&)> onSuccess, std::function<void(const std::error_code&)> onError) { std::thread([=]() { auto ec = performDownload(url); if (ec) { onError(ec); } else { onSuccess("Download completed"); } }).detach(); } // 辅助函数:返回 optional 表示可能无结果 std::optional<std::string> getCachedData(const std::string& key) { auto it = m_cache.find(key); if (it != m_cache.end()) { return it->second; } return std::nullopt; // 缓存未命中,不是错误 } private: bool m_initialized = false; bool m_connected = false; std::map<std::string, std::string> m_cache; enum class ErrorCode { NotInitialized, ConnectionFailed, Timeout }; std::error_code make_error_code(ErrorCode ec) { // 实现 error_code 创建 return std::error_code(); } bool loadConfiguration(const std::string&) { return true; } bool initializeSSL() { return true; } std::error_code performConnection(const std::string&, int) { return std::error_code(); } std::error_code performDownload(const std::string&) { return std::error_code(); } };

四、总结:决策矩阵

场景推荐方式原因
构造函数失败异常对象无法正常构造,没有有效状态可返回
严重违反前置条件异常函数契约被破坏,无法保证正确执行
资源获取失败异常通常无法继续正常操作
预期内业务逻辑失败错误码/枚举如登录失败、文件不存在,用户需要处理
需要区分多种错误类型错误码/自定义类型用户需要根据错误类型做不同处理
高性能/高频调用错误码异常有性能开销
异步操作回调 + 错误码异常无法跨线程传播
可选值/可能不存在std::optional表示"无值"不是错误
库内部错误异常 + 文档内部错误用户无法处理,应终止

黄金法则

  • 异常用于意外、不可恢复的错误

  • 错误码用于预期、可恢复的错误

  • 自定义类型用于需要区分错误类型的场景

  • Optional 用于"无值"是正常情况

http://www.jsqmd.com/news/433115/

相关文章:

  • 解读大数据领域结构化数据的性能优化策略
  • YOLO11 改进 - C2PSA _ C2PSA融合Mask Attention掩码注意力,可学习掩码矩阵破解低分辨率特征提取难题 _ 2025 预印
  • 计算资源与AI模型性能提升的关系探讨
  • AI检测会对论文进行误判吗?
  • cf div2 1078 F1
  • 2026城固装修公司排名TOP5权威测评|城固哪家装修公司靠谱?性价比高口碑好首选金匠装饰 - 一个呆呆
  • Python核心语法-Python关键字 - 努力-
  • YOLO11 改进 - C2PSA _ C2PSA融合MSLA多尺度线性注意力(Arxiv2025 ):并行多分支架构融合上下文语义,提升特征判别力
  • 元宵节猜灯谜答题闯关抽奖H5抖音快手微信小程序看广告流量主开源
  • YOLO11 改进 - C2PSA _ C2PSA融合Mona多认知视觉适配器(CVPR 2025):打破全参数微调的性能枷锁:即插即用的提点神器,引领视觉微调新突破
  • react遇坑记
  • 大数据领域存算分离的自动化运维实践
  • Python核心语法-数据类型 - 努力-
  • YOLO11 改进 - C2PSA _ C2PSA融合DiffAttention差分注意力:轻量级差分计算实现高效特征降噪,提升模型抗干扰能力
  • 解锁企业知识图谱的“黑匣子”:OntoEKG重塑本体构建范式,AI赋能数据价值释放
  • YOLO11 改进 - C2PSA EDFFN高效判别频域前馈网络(CVPR 2025):频域筛选机制增强细节感知,优化复杂场景目标检测
  • 高通全新可穿戴芯片组或终结智能手机主导地位
  • YOLO11 改进 - C2PSA _ C2PSA融合EDFFN高效判别频域前馈网络(CVPR 2025):频域筛选机制增强细节感知,优化复杂场景目标检测
  • 大数据处理中的并行计算:原理与性能调优
  • 【预测模型】多种智能算法优化深度极限学习机(GWO-DELM/MVO-DELM/WDO-DELM)Matlab实现
  • 5种光伏MPPT算法(电导法、变步长扰动法、粒子群PSO、恒压法CVT、定步长扰动法)Matlab仿真
  • YOLO11 改进 - C2PSA _ C2PSA融合DML动态混合层(Dynamic Mixing Layer)轻量级设计优化局部细节捕获与通道适应性,提升超分辨率重建质量
  • 贾子(Kucius)思想纲领 |The Program of Kucius Thought
  • 服务器频繁崩溃背后的意外真相:一个膝盖惹的祸
  • 【优化求解】基于改进离散狼群算法的火力分配附Matlab代码
  • 35岁程序员转行大模型?一篇说清实操方法,非常详细建议收藏
  • 边缘计算场景:在受限资源设备上部署DeepSeek的可行性
  • 孩子近视逐年加深,该如何科学护眼防近视?
  • OpenClaw 深度拆解:从本地 AI 助理,看透企业级 Agent 的 17 层终极架构
  • ubuntu25.10查看主板与内存信息