ShuangChenYue ShuangChenYue
首页
  • Cpp之旅
  • Cpp专栏
  • Effective_CPP
  • muduo网络库
  • Unix环境高级编程
  • Cpp提高编程
  • 计算机网络
  • 操作系统
  • 数据结构
  • Linux
  • 算法
  • 基础篇
  • MySql
  • Redis
  • 电子嵌入式通信协议
  • 深入浅出SSD
  • 文件系统
  • 汇编语言
  • STM32
  • 随笔(持续更新)
  • Git知识总结
  • Git 创建删除远程分支
  • nvm使用小结
  • 虚拟机固定 IP 地址
  • Shell 脚本学习笔记
  • VScode 插件 CodeGeeX 使用教程
  • KylinV10 将项目上传至 Github教程
  • KylinV10 安装 MySQL 教程(可防踩雷)
  • kylinV10-SP1 安装 QT
  • 高并发内存池
  • USBGUARD 项目编译环境配置
  • Power_Destory 项目
  • U 盘清除工具编译教程
  • 个人博客代码推送教程
  • HTML与CSS
  • JS学习
  • Vue3入门
  • Vue3进阶
  • 黑马Vue3
  • MFC编程随记
  • MFC实现ini配置文件的读取
  • MFC实现点击列表头排序
  • 贴图法美化Button按钮
  • 如何高效阅读嵌入式项目代码
  • NAND Flash
  • ARM 处理器
  • 嵌入式基础知识-存储器
  • 闪存存储和制造技术概述
  • 芯片IO驱动力
  • 主流先进封装技术介绍
  • 虎牙C++技术面经
  • 金山一面复习
  • 完美世界秋招 C++ 游戏开发面经(Cpp部分)
  • 博客搭建
  • 网站收藏箱
首页
  • Cpp之旅
  • Cpp专栏
  • Effective_CPP
  • muduo网络库
  • Unix环境高级编程
  • Cpp提高编程
  • 计算机网络
  • 操作系统
  • 数据结构
  • Linux
  • 算法
  • 基础篇
  • MySql
  • Redis
  • 电子嵌入式通信协议
  • 深入浅出SSD
  • 文件系统
  • 汇编语言
  • STM32
  • 随笔(持续更新)
  • Git知识总结
  • Git 创建删除远程分支
  • nvm使用小结
  • 虚拟机固定 IP 地址
  • Shell 脚本学习笔记
  • VScode 插件 CodeGeeX 使用教程
  • KylinV10 将项目上传至 Github教程
  • KylinV10 安装 MySQL 教程(可防踩雷)
  • kylinV10-SP1 安装 QT
  • 高并发内存池
  • USBGUARD 项目编译环境配置
  • Power_Destory 项目
  • U 盘清除工具编译教程
  • 个人博客代码推送教程
  • HTML与CSS
  • JS学习
  • Vue3入门
  • Vue3进阶
  • 黑马Vue3
  • MFC编程随记
  • MFC实现ini配置文件的读取
  • MFC实现点击列表头排序
  • 贴图法美化Button按钮
  • 如何高效阅读嵌入式项目代码
  • NAND Flash
  • ARM 处理器
  • 嵌入式基础知识-存储器
  • 闪存存储和制造技术概述
  • 芯片IO驱动力
  • 主流先进封装技术介绍
  • 虎牙C++技术面经
  • 金山一面复习
  • 完美世界秋招 C++ 游戏开发面经(Cpp部分)
  • 博客搭建
  • 网站收藏箱
  • Cpp之旅

    • 第1章 基础
    • 第2章 用户自定义类型
    • 第3章 模块化
    • 第4章 错误处理
      • 4.1 异常
      • 4.2 断言机制
        • 4.2.1 assert
        • 4.2.2 static_assert
        • 4.2.3 noexcept
      • 4.3 建议
    • 第5章 类
    • 第6章 基本操作
    • 第7章 模板
    • 第8章 概念和泛型编程
    • 第9章 标准库
  • Cpp专栏

  • Effetcive_CPP

  • muduo网络库

  • Unix环境高级编程

  • Cpp提高编程

  • CPP语言
  • Cpp之旅
霜晨月
2023-11-24
目录

第4章 错误处理

# Cpp之旅(学习笔记)第4章 错误处理

# 4.1 异常

当我们试图越界访问Vector动态数组时,应该发生什么?

假定可从下标越界的访问错误中恢复,那么Vector类的解决方案是实现者检测所有的越界访问并且告知用户。然后用户执行合适的操作。

例如:Vector::operator可以检测所有越界访问并且抛出out_of_range异常:

double& Vector::operator[](int i) {
    if(!(0 < i && i < size()))
        throw out_of_range{"Vector::operator[]"};
    return elem[i];
}
1
2
3
4
5
  • throw指令创建了一个 out_of_range 类型的异常,并将异常的控制权转移给直接或者间接调用 Vector::operator[]() 函数的用户。
  • 要做到这点,编译器的实现需要回溯函数的调用栈并且找到调用者的上下文。
  • 这意味着异常处理机制将退出当前作用域并且把上下文回溯到对该异常感兴趣的调用者,在这个过程中可能会调用析构函数。
void f(Vector& v)
{
    //...
    try{							//在这个区块抛出的out_of_range异常,使用下方处理器处理
        compute1(v);				//可能会试图访问v的结束以外的范围
        Vector v2 = compute2(v);	//可能会试图访问v的结束以外的范围
        compute3(v2);				//可能会试图访问v2的结束以外的范围
    }
    catch(const out_of_range& err){//哎呀,发生了out_of_range错误
        //处理这个错误
        cerr << err.what() << '\n';
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
  • 将想要捕获异常的代码放进一个try代码块。此处的catch语句用来处理类型为 out_of_range 的异常。
  • out_of_range 类型在标准库<stdexcept>中定义,事实上也已经被一些容器访问类标准库函数使用。
  • 这里使用引用来捕获异常以避免对异常变量的复制,同时使用 what() 函数打印输出被 throw 抛出的错误信息。

异常处理机制可以是错误处理更简单、更系统化,同时提升可读性。

让异常处理变得简单与系统化的主流技术(名为资源获取即初始化,RAII)。

RAII的基本思想:让构造函数负责获取类需要的资源,同时让析构函数负责释放资源,这样就可让资源释放可靠地自动进行。

# 4.2 断言机制

# 4.2.1 assert

标准库提供了一个调试宏,assert(),它可以在运行时断言必须满足的条件。

例如:

void f(const char* p) {
    assert(p != nullptr);//p不可以是nullptr
    // ...
}
1
2
3
4

如果assert()的条件不满足,在调试模式下,程序终止,在非调试模式下,assert()不被检查。这个功能简单粗暴而且不够灵活,当然也比什么都不做要强。

# 4.2.2 static_assert

异常用于报告在运行时发现的错误。但只要有可能,我们倾向于尽量让错误可以在编译时被发现。

static_assert(4 <= sizeof(int), "integers are too small");//检查整数大小
1

如果4 <= sizeof(int)不满足(意思是:如果系统中的int类型不具备至少4字节长度)。则会输出integers are too small。我们把这种语句叫做断言。

静态断言机制可以使用任何表达式:

constexpr double C = 299792.458;					// km/s
void f(double speed) {
    constexpr double local_max = 160.0/(60*60);		// 160km/h = 160.0/(60*60)km/s
    static_assert(speed < C, "can't go that fast");	// 错误:speed必须是常量
    static_assert(local_max < C, "can't go that fast");// 可行
    //...
}
1
2
3
4
5
6
7
  • 一般来说,如果A不为真,那么static_assert(A,S)打印输出S作为编译错误信息。
  • 如果你不需要某个特定的信息,可以忽略S参数,编译器会生成默认信息:static_assert(4 <= sizeof(int));//使用默认信息
  • 典型的默认信息通常由static_assert调用代码的位置加上断言内容的字符表述谓词构成。
  • 静态断言的一大重要用途是在泛型编程中对类型参数进行断言。

# 4.2.3 noexcept

如果一个函数绝不应当抛出异常,那么可以将它声明为noexcept。

void user(int sz) noexcept
{
    Vector v(sz);
    iota(&v[0],&v[sz],1);//将v填充为1,2,3,4...
    //...
}
1
2
3
4
5
6

如果所有的意图与设计都失败,user()函数依然抛出异常,系统会立即调用std::terminate中止这个程序。

# 4.3 建议

  1. 打开文件失败或到达迭代结束是预期事件而不是异常;
  2. 在错误需要通过多层函数调用向上渗透时,抛出异常;
  3. 如果不确定是使用异常还是错误代码,首选异常;
  4. 优先使用RAII,而不是直接用try代码块;
  5. 能在编译时检查的问题尽量在编译时检查;
  6. 使用断言机制对故障进行单点控制;
  7. Concepts是编译时的断言,因此经常用在断言中;
  8. 如果你的函数不允许抛出异常,那么把它声明为noexcept;
  9. 除非经过全面考虑,否则不要使用noexcept;
上次更新: 2024/6/3 14:54:44
第3章 模块化
第5章 类

← 第3章 模块化 第5章 类→

Theme by Vdoing | Copyright © 2023-2024 霜晨月
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式