异常与c++11中的noexcept【c++】
文章目录
- 异常
- try catch 语法
- 执行流程:
- 项目中如何使用try catch
- noexcept
- 如何解决异常处理中的资源泄漏
- RAII
- 自定义 RAII 类管理其他资源(文件、锁、网络连接等)
异常
try catch 语法
将 “可能出错的代码” 与 “错误处理逻辑” 分离
try
负责 “发现问题”,catch
负责 “解决问题”
#include <iostream>
#include <stdexcept>// 可能抛出异常的函数
void read_file(const std::string& filename) {// 模拟“文件不存在”的错误,主动抛出异常if (filename.empty()) {throw std::invalid_argument("文件名不能为空"); // 抛出异常}// 正常逻辑(略)
}int main() {std::string filename = ""; // 无效的文件名try {// try块:标记“可能抛异常”的代码read_file(filename); // 调用可能抛异常的函数std::cout << "文件读取成功(这行不会执行,因为上面抛了异常)" << std::endl;} // catch块1:处理“参数无效”类型的异常catch (const std::invalid_argument& e) {std::cerr << "处理参数错误:" << e.what() << std::endl; // 输出:处理参数错误:文件名不能为空} // catch块2:处理所有其他标准异常(兜底)catch (const std::exception& e) {std::cerr << "处理其他错误:" << e.what() << std::endl;}// 异常处理完成后,继续执行后续代码std::cout << "程序继续运行..." << std::endl;return 0;
}
执行流程:
- 执行
try
块:程序先执行try
内部的代码(通常是可能出现错误的操作,如文件读写、网络请求、内存分配等)。 - 异常抛出:若
try
块中执行到throw
语句(如throw std::runtime_error("出错了")
),则立即终止try
块的后续执行,进入 “异常捕获” 阶段。 - 匹配
catch
块:程序从上到下检查catch
块的 “异常类型”,找到与抛出异常类型匹配的catch
块,执行其中的错误处理逻辑。 - 无异常情况:若
try
块中未抛出任何异常,则所有catch
块都会被跳过,程序继续执行catch
块之后的代码
try
本身不处理错误,它的作用是标记一段可能抛出异常的代码范围
在 C++ 异常处理中,e.what() 返回一个描述异常原因的字符串
在实际项目中 ,exception一般是自定义Exception,继承公司的异常规范体系,Exception重写基类(公司的异常规范体系)的虚函数
try
{// 可能抛出异常的代码
} catch (const std::exception& e)
{ // e 是捕获到的异常对象// 处理异常
}
项目中如何使用try catch
try_catch/try_catch.md · beihangya/c++4 - 码云 - 开源中国
noexcept
noexcept
是 C++11 引入的关键字,用于指定函数是否可能抛出异常
1 、noexcept
:表示函数绝对不会抛出任何异常(也不会允许内部异常传播到外部)。
例:
void func() noexcept { // 函数体内的操作不会抛出异常,或会捕获所有内部异常
}
2、noexcept(表达式)
:表示函数是否抛出异常由括号内的 “常量表达式” 结果决定(true
表示不抛,false
表示可能抛)
// 若 T 的移动构造函数不抛异常,则此函数也不抛
template<typename T>
void move_obj(T& a, T& b) noexcept(noexcept(T(std::move(a))))
{b = std::move(a);
}
如何解决异常处理中的资源泄漏
有泄漏风险
void func() {int* ptr = new int(10); // 手动分配内存try {// 若此处抛出异常,delete 不会执行,导致内存泄漏throw std::runtime_error("出错了");delete ptr; // 正常情况下释放内存} catch (...) {// 未处理 ptr 的释放}
}
RAII
#include <memory>void func() {// 智能指针在构造时获取资源,析构时自动释放(即使发生异常)std::unique_ptr<int> ptr(new int(10)); try {throw std::runtime_error("出错了");// 无需手动 delete,ptr 超出作用域时自动释放} catch (...) {// 异常处理,ptr 仍会在函数结束时释放}
}
自定义 RAII 类管理其他资源(文件、锁、网络连接等)
非内存资源(如文件句柄、互斥锁、数据库连接),可封装成 RAII 类,在析构函数中释放资源
#include <cstdio>
#include <stdexcept>// RAII 类:封装文件句柄,析构时自动关闭
class FileGuard {
private:FILE* file;
public:// 构造时打开文件(获取资源)FileGuard(const char* filename, const char* mode) {file = fopen(filename, mode);if (!file) {throw std::runtime_error("文件打开失败");}}// 析构时关闭文件(释放资源,异常安全)~FileGuard() noexcept { // 析构函数不抛异常if (file) {fclose(file); // 确保文件被关闭}}// 禁用拷贝(避免资源被多次释放)FileGuard(const FileGuard&) = delete;FileGuard& operator=(const FileGuard&) = delete;// 提供文件操作接口FILE* get() const { return file; }
};// 使用 RAII 类,无需手动关闭文件
void write_file() {try {FileGuard file("test.txt", "w"); // 打开文件fprintf(file.get(), "hello"); // 操作文件throw std::runtime_error("写入中出错"); // 模拟异常// 无需手动 fclose,FileGuard 析构时自动关闭} catch (const std::exception& e) {std::cerr << e.what() << std::endl;// 异常发生时,file 仍会被析构,文件正常关闭}
}