C++ 异常

目录

一、c语言传统的处理错误方式

二、c++异常

三、异常的抛出和匹配原则

四、在函数调用链中异常栈展开匹配原则

五、实践运用

六、异常规范

七、异常优点

八、异常缺点

九、常见异常类型


一、c语言传统的处理错误方式

1、终止程序,如assert断言。
2、返回错误码:返回一个编号,这个编号对应某个错误,需要你自己去查。

二、c++异常

异常是一种处理问题的方式,当一个函数发现自己无法处理的错误时,就可以抛出异常,告诉你错了什么,错在哪里,然后让函数的直接/间接调用者来处理这个错误。
为了实现异常,需要增加三个关键字:throw,catch,try.
throw:当出现异常时,程序会抛出一个异常。(本质是抛出一个对象,可以抛出任意类型的对象,例如char、int或者自定义类型均可。)
如果有一个块抛出异常,捕获异常的方法会用到try和catch关键字。
try:内部放置可能会出现异常的代码,try块中的代码被称为保护代码。
catch:捕获异常,内置对异常的处理代码。
语法使用格式如下:

try

{ // 保护代码 (有可能会出错的代码)}

catch( ExceptionName e1 )

{ // catch 块(处理异常的代码) }

catch( ExceptionName e2 )

{ // catch 块 }

catch( ExceptionName eN )

{ // catch 块 }

一个try块内可能会抛出多个异常,就需要多个catch来捕获
每一个catch匹配一个类型,catch捕获的机制就是类型匹配。
try如果没有异常发生,就会直接跳过catch

三、异常的抛出和匹配原则

1、异常通常是由抛出对象引发的,该对象的类型决定了应该激活哪个catch的处理代码
2、被选中的处理代码,是调用链中于该对对象类型匹配且离抛出异常位置最近的。
    也就是说即使有多个catch匹配,但是匹配一个,离抛出最近的那个。
3、抛出异常的对象后,会生成一个异常对象的拷贝,因为抛出的异常对象可能是一个局部对象,
    多以会产生一个拷贝对象,这个拷贝的临时对象会被在catch后销毁。
4、catch(...)可以捕获任何类型的异常,只是不知道异常的错误具体是什么
5、实际中抛出和捕获的匹配原则有个例外,并不都是类型完全匹配的,可以抛出的派生类对象。

四、在函数调用链中异常栈展开匹配原则

1、首先检查throw本身是否在try块内部,(我自己抛,我就在原地处理)如果是,则在再检查匹配的catch。如果有匹配的catch,则到调用catch的地方处理
2、如果没有匹配,则退出当前栈,继续在调用栈中进行查找匹配的catch
3、如果到了main函数的栈帧,依旧没有匹配的catch,就会终止程序。
上述沿着调用链查找匹配catch的过程成为栈展开。
所以,我们在main函数最后都要加一个catch(...)来捕获任意异常类型,否则没有捕获到异常,程序将会终止。
因为异常必须被处理。否则程序就会被直接终止。
一般异常都是在外层去捕获。 
4、找到匹配的catch处理异常后,会继续沿着catch语句后面的代码继续执行。
    也就是异常捕获成功后,后面的程序正常执行。

五、实践运用

但是上述的捕获异常的方式,比较麻烦。
为什么?
因为一个项目的运行,可能会抛出各种类型的异常,
这就导致我们的外层程序员手忙脚乱,冷不丁就会那里出异常。
而且即使是catch(...)这种方式,抛出的是未知异常,无法彻底解决。
那么,到底怎么办呢?

可以抛出派生类对象,捕获基类对象。(实践当中非常好用)
抛出的都是派生类,这里就使用了多态的特性
1、虚函数重写
2、父类/基类的指针/引用调用
抛出那个派生类的异常就会调用那个的虚函数。

//这种格式代表:捕获什么,抛出什么
catch(...)
{
throw ;
}

不要在构造函数和析构函数中抛出异常:
有可能回导致某些位置没有被初始化或者
某些位置没有被析构

六、异常规范

为了让函数使用者知道该函数可能回抛出的异常有那些:
1、可以在函数后面接throw(类型),括号中列出可能抛出的所有异常类型。
2、函数的后面接throw(),表示不抛出异常
3、若没有异常接口声明,该函数可能回抛出任何类型的异常。
c++新增规范:noexcept
在函数后面加上noexcept表示不会抛出异常。
没有写,有可能回抛出异常

七、异常优点

1、清晰准确的描述错误的信息,帮助更好的定位程序的bug
2、传统返回错误代码编号,需要到层层返回,到代码的最外层才能获取错误,
但是异常捕获直接就跳到catch位置返回和处理。
3、更好的处理一些函数错误。例如越界,传统只能终止程序,而且无法返回错误编码。
异常就可以很好的处理

八、异常缺点

1、程序执行流乱跳,比较混乱,难以追踪调试
2、额外性能开销(但基本忽略不计)
3、c++没有垃圾回收机制,资源自己管理,异常很容易导致内存泄露,死锁等问题
4、c++标准库的异常体系不太好,导致大家各自定义各自的异常体系,混乱。
5、有了异常以后,会导致异常使用不规范,程序的总体管理、运维、沟通成本上升。
 

九、常见异常类型

异常类 描述
std::exception 所有标准异常的基类。
std::bad_alloc 在动态分配内存时,当无法分配所需内存时抛出。
std::bad_cast 在 dynamic_cast 运算中,转换目标类型不合法时抛出。
std::bad_exception 用于处理未被 throw 表达式捕获的异常类型。
std::bad_typeid 在 typeid 运算无法获取到有效的类型信息时抛出。
std::logic_error 非临界性的逻辑错误,可以通过修改程序来避免。
std::domain_error 当参数超出有效域时抛出,例如在数学函数中使用了无效的参数。
std::invalid_argument 当提供的参数值不适合操作时抛出。
std::length_error 当试图创建一个超出该容器最大长度的对象时抛出。
std::out_of_range 当访问超出有效范围的对象时抛出,例如数组越界访问。
std::runtime_error 用于表示可以通过处理来避免的运行时错误。
std::overflow_error 当数值运算超出数值类型可以表示的范围时抛出。
std::underflow_error 当数值运算结果太小无法表示时抛出。

相关推荐

  1. <span style='color:red;'>C</span>++<span style='color:red;'>异常</span>

    C++异常

    2024-07-14 18:56:01      46 阅读
  2. <span style='color:red;'>C</span>++<span style='color:red;'>异常</span>

    C++异常

    2024-07-14 18:56:01      25 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-14 18:56:01       67 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-14 18:56:01       72 阅读
  3. 在Django里面运行非项目文件

    2024-07-14 18:56:01       58 阅读
  4. Python语言-面向对象

    2024-07-14 18:56:01       69 阅读

热门阅读

  1. 嵌入式是Linux:shell使用解析

    2024-07-14 18:56:01       26 阅读
  2. 力扣题解(不同的子序列)

    2024-07-14 18:56:01       27 阅读
  3. 1820D-The Butcher

    2024-07-14 18:56:01       23 阅读
  4. 第二节 shell脚本基础(1)(2)

    2024-07-14 18:56:01       17 阅读
  5. 序列化和反序列化

    2024-07-14 18:56:01       20 阅读
  6. flask基础配置详情

    2024-07-14 18:56:01       16 阅读
  7. 昇思25天学习打卡营第24天|RNN实现情感分类

    2024-07-14 18:56:01       20 阅读
  8. Windows图形界面(GUI)-DLG-C/C++ - 对话框的创建实现

    2024-07-14 18:56:01       19 阅读