转自:【C++学习笔记】C++异常处理!你绝对不能错过的干货! - 知乎

合理地使用C++异常处理,能够使我们写出来的程序更加稳定强健,不易崩溃。那么,应该如何使用C++异常处理呢?下面,我们就来向大家介绍最简单也是最常用的异常处理语句 。

一、什么是异常

用官方的话来说就是程序在执行过程中产生的问题,换句通俗的话来讲就是程序执行的出现的异常,比如程序崩了、内存泄漏了、数组越界以及其他异常信息的出现。

异常提供了一种转移程序控制权的方式。C++ 异常处理涉及到三个关键字:try、catch、throw

throw: 当问题出现时,程序会抛出一个异常。这是通过使用 throw 关键字来完成的。

catch: 在您想要处理问题的地方,通过异常处理程序捕获异常。catch 关键字用于捕获异常。

try: try 块中存放可能出现异常的代码。它后面通常跟着一个或多个 catch 块。

如果有一个块抛出一个异常,捕获异常的方法会使用 try 和 catch 关键字。try 块中放置可能抛出异常的代码,try 块中的代码被称为保护代码。使用 try/catch 语句的语法如下所示:

try
{
// 保护代码
}catch( ExceptionName e1 )
{
// catch 块
}catch( ExceptionName e2 )
{
// catch 块
}catch( ExceptionName eN )
{
// catch 块
}

如果 try 块在不同的情境下会抛出不同的异常,这个时候可以尝试罗列多个 catch 语句,用于捕获不同类型的异常。

二、抛出异常

您可以使用 throw 语句在代码块中的任何地方抛出异常。throw 语句的操作数可以是任意的表达式,表达式的结果的类型决定了抛出的异常的类型

以下是尝试除以零时抛出异常的实例:

double division(int a, int b)
{
if( b == 0 )
{
throw "Division by zero condition!";
}
return (a/b);
}

三、捕获异常

catch 块跟在 try 块后面,用于捕获异常。您可以指定想要捕捉的异常类型,这是由 catch 关键字后的括号内的异常声明决定的。

try
{
// 保护代码
}catch( ExceptionName e )
{
// 处理 ExceptionName 异常的代码
}

上面的代码会捕获一个类型为 ExceptionName 的异常。如果您想让 catch 块能够处理 try 块抛出的任何类型的异常,则必须在异常声明的括号内使用省略号 ...,如下所示:

try
{
// 保护代码
}catch(...)
{
// 能处理任何异常的代码
}

下面是一个实例,抛出一个除以零的异常,并在 catch 块中捕获该异常。

#include <iostream>
using namespace std;
double division(int a, int b)
{
if( b == 0 )
{
throw "Division by zero condition!";
}
return (a/b);
}
int main ()
{
int x = 50;
int y = 0;
double z = 0;
try {
z = division(x, y);
cout << z << endl;
}catch (const char* msg) {
cerr << msg << endl;
}
return 0;
}

由于我们抛出了一个类型为 const char* 的异常,因此,当捕获该异常时,我们必须在 catch 块中使用 const char*。当上面的代码被编译和执行时,它会产生下列结果:

Division by zero condition!

四、异常的处理规则

throw抛出的异常类型与catch抓取的异常类型要一致

throw抛出的异常类型可以是子类对象,catch可以是父类对象;

catch块的参数推荐采用地址传递而不是值传递,不仅可以提高效率,还可以利用对象的多态性。另外,派生类的异常捕获要放到父类异常扑获的前面,否则,派生类的异常无法被扑获;

如果使用catch参数中,

使用基类捕获派生类对象,一定要使用传递引用的方式

例如catch (exception &e);

异常是通过抛出对象而引发的,该对象的类型决定了应该激活哪个处理代码;

被选中的处理代码是调用链中与该对象类型匹配且离抛出异常位置最近的那一个;

在try的语句块内声明的变量在外部是不可以访问的,即使是在catch子句内也不可以访问;

栈展开会沿着嵌套函数的调用链不断查找,直到找到了已抛出的异常匹配的catch子句。如果抛出的异常一直没有函数捕获(catch),则会一直上传到c++运行系统那里,导致整个程序的终止。

五、C++ 标准的异常

C++ 提供了一系列标准的异常,定义在 <exception> 中,我们可以在程序中使用这些标准的异常。它们是以父子类层次结构组织起来的,如下所示:

下表是对上面层次结构中出现的每个异常的说明:

异常描述

六、定义新的异常

您可以通过继承和重载 exception 类来定义新的异常。下面的实例演示了如何使用 std::exception 类来实现自己的异常:

实例

#include <iostream>
#include <exception>
using namespace std;
struct MyException : public exception
{
  const char * what () const throw () { return "C++ Exception";
  }
};

int main()
{
  try
  {
        throw MyException();
   }
  catch(MyException& e)
  {
        std::cout << "MyException caught" << std::endl;
        std::cout << e.what() << std::endl;
  }
  catch(std::exception& e)
  {
        //其他的错误
   }
}

这将产生以下结果:

MyException caught
C++ Exception

在这里,what() 是异常类提供的一个公共方法,它已被所有子异常类重载。这将返回异常产生的原因。

七、总结

1. 使用异常处理的优点:

传统错误处理技术,检查到一个错误,只会返回退出码或者终止程序等等,我们只知道有错误,但不能更清楚知道是哪种错误。使用异常,把错误和处理分开来,由库函数抛出异常,由调用者捕获这个异常,调用者就可以知道程序函数库调用出现的错误是什么错误,并去处理,而是否终止程序就把握在调用者手里了。

2. 使用异常的缺点:

如果使用异常,光凭查看代码是很难评估程序的控制流:函数返回点可能在你意料之外,这就导致了代码管理和调试的困难。启动异常使得生成的二进制文件体积变大,延长了编译时间,还可能会增加地址空间的压力。

C++没有垃圾回收机制,资源需要自己管理。有了异常非常容易导致内存泄漏、死锁等异常安全问题。 这个需要使用RAII来处理资源的管理问题。学习成本较高。

C++标准库的异常体系定义得不好,导致大家各自定义各自的异常体系,非常的混乱。

3. 什么时候使用异常?

建议:除非已有的项目或底层库中使用了异常,要不然尽量不要使用异常,虽然提供了方便,但是开销也大。

4. 程序所有的异常都可以catch到吗?

并非如此,只有发生异常,并且又抛出异常的情况才能被 catch 到。例如,数组下标访问越界的情况,系统是不会自身抛出异常的,所以我们无论怎么 catch 都是无效的;在这种情况,我们需要自定义抛出类型,判断数组下标是否越界,然后再根据自身需要throw自定义异常对象,这样才可以catch到异常,并进行进一步处理。

看到这里你是不是对“C++”又有了一点新的认知呢~

如果你喜欢这篇文章的话,动动小指,加个关注哦~

如果你也想成为程序员,想要快速掌握编程,这里为你分享一个学习企鹅圈子

里面有资深专业软件开发工程师,在线解答你的所有疑惑~C语言入门“so easy”

资料包含:编程入门、游戏编程、课程设计、黑客等。

【转】【C++学习笔记】C++异常处理相关推荐

  1. 冰冰学习笔记:异常处理

    欢迎各位大佬光临本文章!!! 还请各位大佬提出宝贵的意见,如发现文章错误请联系冰冰,冰冰一定会虚心接受,及时改正. 本系列文章为冰冰学习编程的学习笔记,如果对您也有帮助,还请各位大佬.帅哥.美女点点支 ...

  2. Python基础学习笔记:异常处理与断言(assertions)的运用

    python 提供了两个重要的功能来处理 python 程序在运行中出现的异常和错误: 异常处理 断言(assertions) 1.异常处理 捕捉异常可以使用 try/except 语句. try/e ...

  3. 【Python学习笔记】异常处理try-except

    Python异常处理 我们一般使用try-except语句来进行异常处理. 使用except Exception as err可以统一捕捉所有异常,而也可以分开处理单个异常. # 分开捕捉单个异常tr ...

  4. c++学习笔记(15) 异常处理

    异常处理概述: 异常是用一个throw语句抛出,同时用try-catch来捕获,例如一个简单的例子: #include <iostream>using namespace std;int ...

  5. 老嘤学习笔记 python异常处理

    异常捕获 捕获异常,忽略报错,让下面的代码逻辑正常运行 报错执行except后面的代码块 没有报错执行else后面的代码块 finally后面的代码块无论是否报错都会执行 这里先输入a,报错 后输入1 ...

  6. Json.Net学习笔记

    Json.Net学习笔记 摘自:  http://www.verydemo.com/demo_c360_i45119.html 分类: 编程语言/ ASP.NET/ 文章 导读:string goog ...

  7. Mini 容器学习笔记4——组件的生命周期(应用篇)

    Mini容器支持6中生命周期类型: 1. Singleton :单利类型(缺省组件都是单利类型的生命周期,由容器进行托管的) [Test]public void SingletonLifestyleT ...

  8. Mini 容器学习笔记6——组件的获取(应用)

    1. 通过组件Id获取组件实例: [Test]public void GetByIdTest(){ServiceRegistry.Register<Person>("person ...

  9. 前阿里大佬干货分享,0基础小白,转行必看Python学习笔记(七)

    Python学习笔记7 异常处理 包和模块 包和模块的一般操作 导入操作的本质 模块检索的路径 导入模块的场景 第三方包和模块的安装 异常处理 系统内部一开始已经内置了一些特定的错误场景,当我们触发了 ...

  10. Windows异常学习笔记(二)—— 内核异常处理流程用户异常的分发

    Windows异常学习笔记(二)-- 内核异常处理流程&用户异常分发 用户层与内核层异常 内核异常 分析 KiDispatchException 分析 RtlDispatchException ...

最新文章

  1. 10大高性能开发宝石,我要消灭一半程序员!
  2. linux系统的编译原理,GCC编译原理_Linux编程_Linux公社-Linux系统门户网站
  3. 【FFmpeg】使用 FFmpeg 处理音视频格式转换流程 ( 解复用 | 解码 | 帧处理 | 编码 | 复用 )
  4. Zookeeper的典型应用场景(2)
  5. LeetCode 157. 用 Read4 读取 N 个字符
  6. dmesg的时间戳转换为对应的时间
  7. Python学习笔记:目录与文件操作
  8. [程序设计]C++中extern “C”含义深层探索(转载)
  9. xmldocument的使用
  10. mybaitis List入参
  11. LRU缓存介绍与实现 (Java)
  12. Math类的常用方法
  13. python实现BP神经网络
  14. IT桌面运维常识系列 -(Windows部署服务 - 01)
  15. 自主招生计算机系面试,各大高校自主招生“扎堆”六月 北大自主招生面试考题公布...
  16. linux tar.7z如何解压,(转)Linux下解压:tar、rar、7z命令
  17. 数据库事务ACID四大特性:原子性、一致性, 隔离性, 持久性
  18. 给服务器下载补丁及安装补丁
  19. _pickle.UnpicklingError: unpickling stack underflow
  20. 从“蛙步”到“雁行”vivo的新周期与新常态

热门文章

  1. android 数组增加,java-添加视图数组会使android应用崩溃
  2. python 字符串format使用
  3. CNN(卷积神经网络)、RNN(循环神经网络)、DNN(深度神经网络)的内部网络结构区别...
  4. Golang tcp转发 remoteAddr错误
  5. 看动画轻松理解时间复杂度(一)
  6. Linux卸载MariaDB
  7. UVA 1645 - Count(简单DP)
  8. 07.30《jQuery》——1.1DOM对和jQuery对象的转化
  9. jmeter自定义并发用户数图形插件介绍
  10. 如何配置一个最基本的web富文本编辑器?--之wangEditor(验证成功)