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

【C++进阶】异常

【C++进阶】异常

1. 传统错误处理方式

在C++之前,C语言主要使用两种错误处理方式:

1.1 终止程序

#include <cassert>void traditional_approach1() {int* ptr = malloc(sizeof(int) * 100);assert(ptr != nullptr);  // 如果分配失败,直接终止程序// 使用内存...free(ptr);
}

缺点:用户体验差,程序突然崩溃。

1.2 返回错误码

#include <cerrno>
#include <cstring>int traditional_approach2() {FILE* file = fopen("nonexistent.txt", "r");if (file == nullptr) {printf("错误: %s\n", strerror(errno));  // 返回错误码return -1;}// 处理文件...fclose(file);return 0;
}

缺点:需要程序员手动检查错误码,容易遗漏。

2. C++异常概念

C++引入异常机制,提供更优雅的错误处理方式:

  • throw:抛出异常
  • try:尝试执行可能抛出异常的代码
  • catch:捕获并处理异常
#include <iostream>
#include <stdexcept>double divide(int a, int b) {if (b == 0) {throw std::runtime_error("除数不能为零!");}return static_cast<double>(a) / b;
}int main() {try {double result = divide(10, 0);std::cout << "结果: " << result << std::endl;}catch (const std::exception& e) {std::cout << "捕获异常: " << e.what() << std::endl;}return 0;
}

3. 异常的使用

3.1 异常的抛出和捕获

异常匹配原则

  1. 异常通过抛出对象引发,对象类型决定匹配的catch块
  2. 选择调用链中类型匹配且距离最近的catch块
  3. 抛出异常会生成对象拷贝
  4. catch(...) 可捕获任意类型异常
  5. 可以用基类捕获派生类异常(多态)
#include <iostream>
#include <string>void func3() {throw std::string("在func3中发生错误!");
}void func2() {func3();
}void func1() {func2();
}int main() {try {func1();}catch (const std::string& msg) {std::cout << "字符串异常: " << msg << std::endl;}catch (const char* msg) {std::cout << "C字符串异常: " << msg << std::endl;}catch (...) {std::cout << "未知异常" << std::endl;}return 0;
}

3.2 异常的重新抛出

#include <iostream>
#include <memory>void processResource() {auto resource = std::make_unique<int>(42);try {// 模拟可能抛出异常的操作throw std::runtime_error("处理资源时发生错误");}catch (...) {std::cout << "清理资源并重新抛出异常" << std::endl;// 资源会被unique_ptr自动释放throw;  // 重新抛出}
}int main() {try {processResource();}catch (const std::exception& e) {std::cout << "主函数捕获: " << e.what() << std::endl;}return 0;
}

3.3 异常安全

重要准则

  • 构造函数:避免抛出异常,可能导致对象不完整
  • 析构函数:绝对不要抛出异常,可能导致资源泄漏
  • 使用RAII(资源获取即初始化)管理资源
#include <memory>
#include <vector>class DatabaseConnection {
private:std::unique_ptr<int> connection;public:DatabaseConnection() {connection = std::make_unique<int>(1);// 如果这里抛出异常,对象构造失败是安全的}~DatabaseConnection() {// 析构函数不抛出异常// 资源由unique_ptr自动管理}// 禁用拷贝,避免资源管理问题DatabaseConnection(const DatabaseConnection&) = delete;DatabaseConnection& operator=(const DatabaseConnection&) = delete;
};

3.4 异常规范

#include <iostream>// C++98风格:可能抛出int或double异常
void old_style() throw(int, double) {throw 42;
}// C++11风格:不抛出异常
void modern_style() noexcept {// 这个函数保证不会抛出异常
}// 无异常规范:可能抛出任何异常
void any_exception() {throw std::runtime_error("任何异常");
}int main() {try {old_style();}catch (int e) {std::cout << "捕获int异常: " << e << std::endl;}catch (double e) {std::cout << "捕获double异常: " << e << std::endl;}return 0;
}

4. 自定义异常体系

实际项目中通常需要自定义异常体系:

#include <iostream>
#include <string>
#include <memory>// 基础异常类
class Exception {
protected:std::string _errmsg;int _id;public:Exception(const std::string& errmsg, int id): _errmsg(errmsg), _id(id) {}virtual ~Exception() = default;virtual std::string what() const {return _errmsg + " (错误码: " + std::to_string(_id) + ")";}
};// 数据库异常
class SqlException : public Exception {
private:std::string _sql;public:SqlException(const std::string& errmsg, int id, const std::string& sql): Exception(errmsg, id), _sql(sql) {}std::string what() const override {return "SQL异常: " + _errmsg + " -> " + _sql;}
};// 缓存异常
class CacheException : public Exception {
public:CacheException(const std::string& errmsg, int id): Exception(errmsg, id) {}std::string what() const override {return "缓存异常: " + _errmsg;}
};// HTTP服务器异常
class HttpServerException : public Exception {
private:std::string _type;public:HttpServerException(const std::string& errmsg, int id, const std::string& type): Exception(errmsg, id), _type(type) {}std::string what() const override {return "HTTP服务器异常[" + _type + "]: " + _errmsg;}
};// 模拟业务逻辑
void processDatabase() {// 模拟数据库操作可能抛出异常throw SqlException("权限不足", 1001, "SELECT * FROM users");
}void processCache() {// 模拟缓存操作throw CacheException("缓存键不存在", 2001);
}void handleHttpRequest() {// 模拟HTTP请求处理throw HttpServerException("请求超时", 3001, "GET");
}int main() {try {handleHttpRequest();processCache();processDatabase();}catch (const Exception& e) {// 多态处理:一个catch块处理所有派生异常std::cout << e.what() << std::endl;}catch (...) {std::cout << "未知异常" << std::endl;}return 0;
}

5. C++标准库异常体系

C++标准库提供了一套完整的异常体系:

#include <iostream>
#include <stdexcept>
#include <vector>
#include <memory>void demonstrate_standard_exceptions() {try {// 1. bad_alloc - 内存分配失败std::unique_ptr<int[]> huge_array = std::make_unique<int[]>(1000000000000LL);}catch (const std::bad_alloc& e) {std::cout << "内存分配失败: " << e.what() << std::endl;}try {// 2. out_of_range - 下标越界std::vector<int> vec = {1, 2, 3};int value = vec.at(10);  // 抛出std::out_of_range}catch (const std::out_of_range& e) {std::cout << "下标越界: " << e.what() << std::endl;}try {// 3. invalid_argument - 无效参数std::string str("hello");int num = std::stoi(str);  // 可能抛出std::invalid_argument}catch (const std::invalid_argument& e) {std::cout << "无效参数: " << e.what() << std::endl;}try {// 4. logic_error - 逻辑错误throw std::logic_error("逻辑错误示例");}catch (const std::logic_error& e) {std::cout << "逻辑错误: " << e.what() << std::endl;}
}int main() {demonstrate_standard_exceptions();return 0;
}

标准异常类层次结构

std::exception
├── std::bad_alloc
├── std::bad_cast
├── std::bad_typeid
├── std::bad_exception
├── std::logic_error
│   ├── std::domain_error
│   ├── std::invalid_argument
│   ├── std::length_error
│   └── std::out_of_range
└── std::runtime_error├── std::overflow_error├── std::range_error├── std::underflow_error└── std::system_error

6. 异常的优缺点

6.1 优点

1. 清晰的错误信息

// 传统方式
int connectDatabase() {if (/* 连接失败 */) return -1;if (/* 权限不足 */) return -2;return 0;
}// 异常方式
void connectDatabaseWithException() {if (/* 连接失败 */) throw std::runtime_error("数据库连接失败: 网络超时");if (/* 权限不足 */)throw std::runtime_error("数据库连接失败: 权限不足");
}

2. 调用链简化

void deepFunction() {throw std::runtime_error("深层错误");
}void middleFunction() {deepFunction();  // 不需要检查返回值
}void topFunction() {try {middleFunction();}catch (const std::exception& e) {std::cout << "统一处理: " << e.what() << std::endl;}
}

3. 构造函数错误处理

class FileHandler {
private:std::FILE* file;public:FileHandler(const std::string& filename) {file = std::fopen(filename.c_str(), "r");if (!file) {throw std::runtime_error("无法打开文件: " + filename);}}~FileHandler() {if (file) std::fclose(file);}
};

6.2 缺点

1. 性能开销:异常处理有一定性能代价
2. 代码复杂度:可能使控制流难以跟踪
3. 资源管理:需要RAII确保异常安全

7. 推荐用法

7.1 RAII确保异常安全

#include <memory>
#include <fstream>class SafeFile {
private:std::unique_ptr<std::FILE, decltype(&std::fclose)> file;public:SafeFile(const std::string& filename, const std::string& mode): file(std::fopen(filename.c_str(), mode.c_str()), &std::fclose) {if (!file) {throw std::runtime_error("无法打开文件: " + filename);}}void write(const std::string& content) {if (std::fputs(content.c_str(), file.get()) == EOF) {throw std::runtime_error("写入文件失败");}}
};void safeFileOperation() {SafeFile file("data.txt", "w");file.write("安全的数据写入");// 即使抛出异常,文件也会自动关闭
}

7.2 异常规范建议

class RobustClass {
public:// 明确标注不抛异常的函数void simpleOperation() noexcept {// 简单操作,保证不抛异常}// 可能抛异常的函数要明确文档说明/*** @brief 复杂操作,可能抛出std::runtime_error* @throws std::runtime_error 当操作失败时*/void complexOperation() {if (/* 失败条件 */) {throw std::runtime_error("操作失败");}}// 析构函数绝对不抛异常~RobustClass() noexcept {// 清理资源,确保不抛异常}
};

总结

C++异常处理提供了强大的错误处理机制:

  • 优势:清晰的错误信息、简化的错误传播、构造函数错误处理
  • 挑战:性能开销、代码复杂度、资源管理
  • 推荐用法:使用RAII、自定义异常体系、明确的异常规范
http://www.dtcms.com/a/609543.html

相关文章:

  • 《非暴力沟通》马歇尔•卢森堡博士(美)
  • Rust 从零到精通:构建一个专业级命令行工具 greprs
  • 大足网站建设网络营销市场调研的内容
  • CSS3 分页技术解析
  • HTMLElement 与MouseEvent 事件对象属性详解
  • 建设网站都要学些什么手续拍卖网站模板下载
  • 【火语言RPA实战案例】根据ISBN 编码批量查询孔夫子书籍信息,自动导出本地 Excel(附完整脚本)
  • 从零开始理解状态机:C语言与Verilog的双重视角
  • 做软件常用的网站有哪些软件微信怎么做网站推广
  • 设计模式面试题(14道含答案)
  • [智能体设计模式] 第9章 :学习与适应
  • 肇庆市建设局网站西双版纳建设厅网站
  • LingJing(灵境)桌面级靶场平台新增:真实入侵复刻,知攻善防实验室-Linux应急响应靶机2,通关挑战
  • 融合尺度感知注意力、多模态提示学习与融合适配器的RGBT跟踪
  • 基于脚手架微服务的视频点播系统-脚手架开发部分Fast-dfs,redis++,odb的简单使用与二次封装
  • 构建高可用Redis:哨兵模式深度解析与Nacos微服务适配实践
  • Linux -- 线程同步、POSIX信号量与生产者消费者模型
  • 微服务重要知识点
  • 东莞seo建站排名昆山有名的网站建设公司
  • 主从服务器
  • Linux 文件缓冲区
  • Node.js中常见的事件类型
  • Nacos的三层缓存是什么
  • 交通事故自动识别_YOLO11分割_DRB实现
  • 用flex做的网站空间注册网站
  • Vue + Axios + Node.js(Express)如何实现无感刷新Token?
  • 重大更新!Ubuntu Pro 现提供长达 15 年的安全支持
  • 重庆做学校网站公司农村服务建设有限公司网站
  • 尝试本地部署 Stable Diffusion
  • 网站前置审批专项好的用户体验网站