C++核心技术要点《异常处理详解》

本文涉及的产品
实时计算 Flink 版,5000CU*H 3个月
检索分析服务 Elasticsearch 版,2核4GB开发者规格 1个月
大数据开发治理平台 DataWorks,不限时长
简介: C++核心技术要点《try-throw-catch异常处理详解》

1、什么是异常、异常处理?

异常就是程序运行期出现的错误,而异常处理就是对有可能发生错误的地方做出预见性的安排。C++异常处理机制是一个用来有效地处理运行错误的非常强大且灵活的工具,它提供了更多的弹性、安全性和稳固性,克服了传统方法所带来的问题.异常的抛出和处理主要使用了以下三个关键字: try、 throw 、 catch 。常见的一些异常包括:数组下标越界、除数为0、内存不足等等。


2、异常处理机制

(1)主要使用了try...throw...catch这3个关键字,try就是尝试的意思,尝试运行正常的逻辑代码;catch就是捕获,捕获异常;而throw就是抛出,抛出异常状态


(2)我们在有一个可能会出现异常状态的地方,通过检测异常状态出现的条件,如果条件满足就通过throw抛出异常;我们将一个可能抛出异常的函数或者是代码块放在我们的try{ }中去运行,当然你也可以把不会抛出异常的函数或者是代码块放在try{}中运行,这都是不会编译出错的,但是只是没有必要;如果try{ }块中抛出了异常状态,那么这个异常状态就可能会被被后面的catch{ }捕获并进行处理,注意我这里说的只是可能,并没说一定会被捕获,具体后面再说。


(3)try和catch必须是同时出现的,可以是一对一(一个try块对应一个catch块),也可以是一对多(一个try块对应多个catch块),基本思想:主逻辑与异常处理分离

当我们在try块中抛出了异常,那么抛出异常语句的代码之后的代码将不会被运行了。

QQ截图20240611194924.png

(4)从上面的示意图可以看出来,如果我们当前的函数的try抛出的异常没有被下面的catch捕获到,那么这个异常就会被抛向调用这个函数的函数,也就是会向上抛出,

试图通过他的上层来处理这个异常,如果都没能处理这个异常,那么这个异常就会被操作系统处理,操作系统就会进行简单粗暴的处理方法,直接使程序终止退出。

需要理解的是:异常出现被处理之后我们程序也是被终止的,只不过我们的这种终止方式不会显得那么粗暴、无礼。


3、使用方法

1 #include <iostream>
 2 using namespace std;
 3 
 4 static void func(void)
 5 {
 6     throw 1;
 7 }
 8 
 9 int main(void)
10 {
11     try{
12           func();               // 使能够抛出异常的代码放在try块中
13     }
14     catch (int) 
15     {
16         cout << "发生int异常" << endl;
17     }
18     catch (double)
19     {
20         cout << "发生double异常" << endl;
21     }
22     catch (...)   //  ...表示能够捕获任何异常,只能放在最后面,
23     {
24         cout << "发生异常" << endl;
25     }
26 
27     return 0;
28 }

try...catch

(1)catch (...)表示这个catch块能够捕获任何异常,并且只能放在最后一个catch,反正要么就没有这个catch,有就只能放在最后一个catch中,否则编译不能通过;主要是用来处理上面所有的catch中不能处理的异常就由他来处理。


(2)catch (xxx)中捕获的xxx是什么? 可以是数据类型的形式(int),也可以是声明变量的形式(int a);到底用哪种方式关键看你的catch块中是否需要对throw抛出的变量进行操作:

 1 #include <iostream>
 2 using namespace std;
 3 
 4 static void func(void)
 5 {
 6     throw 1;
 7 }
 8 
 9 int main(void)
10 {
11     try{
12         func();
13     }
14     catch (int a)    //  catch (int)
15     {
16         cout << "发生int异常" << endl;
17         cout << a << endl;
18     }
19 
20     return 0;
21 }

所以从catch捕获的对象要么是一个数据类型,要么是一个声明的变量,有点类似于我们的函数参数,如果我们是声明一个函数那么可以在参数列表中不用加上变量名,如果是在定义的时候我们就得加上变量名,因为声明的时候主要是进行类型的匹配,而定义的时候必须会对参数进行操作,所以肯定要加上名字,不然怎么操作这个变量。


(3)throw抛出的是什么? 其实这个问题上面就已经解决了,因为catch捕获的时候是通过数据类型进行捕获的,所以throw抛出的肯定是一个变量。这样才能够catch进行匹配:

1 #include <iostream>
 2 using namespace std;
 3 
 4 /*************************************/
 5 class Exception{
 6 public:
 7     virtual void exception(void) = 0;
 8 };
 9 
10 class BoundsException : public Exception
11 {
12 public:
13     virtual void exception(void) { cout << "异常:数组下标越界" << endl; }
14 };
15 
16 /***************************************************************/
17 
18 static void func(void)
19 {
20 //    throw 1;     //  抛出int类型数据
21 //    throw 0.1;   //  抛出double类型数据
22 //    throw "exception";  // 抛出char类型指针
23 //    throw string("exception");  // 抛出string类型的字符串
24 //    throw new string("exception"); // 抛出string类型的指针
25 //    throw BoundsException();   // 抛出BoundsException类对象,注意这里的原理就是:执行BoundsException()构造函数,因为创建一个类对象就是调用构造函数;当然这里你也可以直接定义一个类对象,再把对象抛出去
26 //    throw new BoundsException();  // 抛出BoundsException类对象指针
27 }
28 
29 int main(void)
30 {
31     try{
32         func();
33     }
34     catch (int)
35     {
36         // 捕获int类型异常
37     }
38     catch (double)
39     {
40         // 捕获double类型异常
41     }
42     catch (char *ptr)
43     {
44         // 捕获char *类型异常
45     }
46     catch (string &str)
47     {
48         // 捕获string &类型异常
49     }
50     catch (string *ptr)
51     {
52         // 捕获string *类型异常
53     }
54     catch (BoundsException &boun)
55     {
56         // 捕获BoundsException &类型异常
57     }
58     catch (BoundsException *ptr)
59     {
60         // 捕获BoundsException *类型异常
61     }
62     catch (...)
63     {
64         cout << "发生异常" << endl;
65     }
66 
67     return 0;
68 }

抛出的异常有哪些

4、扩展用法

(1)什么是重新抛出异常,为什么要重新抛出异常?

在实际的异常处理中,一个catch块可能处理不完,那么就需要其他的catch块再来对这个异常进行处理,那么他的原理就是:在本个catch块中处理完成之后,我们将这个异常重新抛出,交给他的上层catch块去处理,因为我们抛出的异常就是自己捕获到的异常,所以这个异常捕获申明的时候是一个最好是一个引用。注意catch块中重新抛出的异常只能是由他的上层来处理,不能在本函数中的其他catch块中处理,因为在一个函数中同种类型的catch块只能有一个:代码如下:

 1 #include <iostream>
 2 using namespace std;
 3 
 4 int main(void)
 5 {
 6        try{
 7            // 代码块
 8        }  
 9        catch (double &a)
10         {
11               // 处理异常
12               
13               throw;   // 本catch没有处理完成,抛给上层catch处理
14         }
15    
16         return 0;
17 }

(2)使用异常规范

异常规范在函数声明是规定了函数可以抛出且只能抛出哪些异常。空的异常规范保证函数不会抛出任何异常。如果一个函数声明没有指定异常规范,则该函数可以抛出任何类型的异常。 

例1:函数Pop若有异常,只能抛出popOnEmpty和string类型的异常对象 

void pop( int &value ) throw(popOnEmpty, string); 


例2:函数no_problem()保证不会抛出任何异常 

extern void no_problem() throw(); 


例3:函数problem()可以抛出任何类型的异常 

extern void problem();

1 static void func1(void) throw(int, double);   // 只能抛出int  double类型数据
2 static void func2(void) throw();   // 不能抛出异常
3 static void func3(void);    // 可以抛出任何异常

(3)异常处理类

我们定义一个异常处理的基类,这个基类中包含了异常的处理方法,但是是一个纯虚函数,我们通过定义具体的异常类去继承这个基类,同时定义这个具体的异常处理方法,方法名字和基类中的纯虚函数名字一样,然后我们就可以在catch中捕获基类的对象,我们在具体的异常处抛出一个具体异常类,这样就能够在catch中统一处理了:

1 #include <iostream>
 2 using namespace std;
 3 
 4 /*************************************/
 5 class Exception{
 6 public:
 7     virtual void exception(void) = 0;
 8 };
 9 
10 class BoundsException : public Exception
11 {
12 public:
13     virtual void exception(void) { cout << "异常:数组下标越界" << endl; }
14 };
15 
16 class DivisorException : public Exception
17 {
18 public:
19     virtual void exception(void) { cout << "异常:除数为0" << endl; }
20 };
21 
22 class MemoryException : public Exception
23 {
24 public:
25     virtual void exception(void) { cout << "异常:内存不足" << endl; }
26 };
27 
28 /***************************************************************/
29 
30 static void func(void)
31 {
32     throw BoundsException();   // 抛出BoundsException类对象
33 //    throw DivisorException();   // 抛出BoundsException类对象
34 //    throw MemoryException();   // 抛出BoundsException类对象
35 }
36 
37 int main(void)
38 {
39     try{
40         func();
41     }
42     catch (BoundsException &boun)
43     {
44         boun.exception();
45     }
46     catch (...)
47     {
48         cout << "发生异常" << endl;
49     }
50 
51     return 0;
52 }
相关文章
|
24天前
|
C++
C++中类的接口与实现分离的技术性探讨
C++中类的接口与实现分离的技术性探讨
30 1
|
25天前
|
C++
C++代码的可读性与可维护性:技术探讨与实践
C++代码的可读性与可维护性:技术探讨与实践
21 1
|
25天前
|
安全 C++
C++中的异常处理与错误处理机制
C++中的异常处理与错误处理机制
27 0
|
3天前
|
C++
C++一分钟之—异常处理try-catch
【6月更文挑战第22天】C++异常处理机制,借助`try`、`catch`、`throw`管理错误,优雅处理异常,防止程序崩溃。`try`包围可能出错的代码,`catch`捕获异常,`throw`引发异常。基本结构是:`try-catch`块中,未捕获的异常将向上抛出。多`catch`块可按顺序捕获不同类型的异常。易错点包括忽视异常传播、不精确的`catch`和资源未清理。通过精确捕获、RAII技术和适当的异常策略,提升代码健壮性和效率。
5 1
|
13天前
|
C++
C++核心技术要点《友元函数,友元类》
C++核心技术要点《友元函数,友元类》
20 3
|
13天前
|
C++ 索引
C++核心技术要点《运算符重载》
C++核心技术要点《运算符重载》
22 2
|
13天前
|
C++
c++语言核心技术要点,《运行时类型识别RTTI》
c++语言核心技术要点,《运行时类型识别RTTI》
17 2
|
13天前
|
存储 算法 安全
用C++打造极致高效的框架:技术探索与实践
本文探讨了如何使用C++构建高性能框架。C++凭借其高性能、灵活性和跨平台性成为框架开发的理想选择。关键技术和实践包括:内存管理优化(如智能指针和自定义内存池)、并发编程(利用C++的并发工具)、模板与泛型编程以提高代码复用性,以及性能分析和优化。在实践中,应注意代码简洁性、遵循最佳实践、错误处理和充分测试。随着技术发展,不断提升对框架性能的要求,持续学习是提升C++框架开发能力的关键。
24 1
|
24天前
|
C++
C++中使用namespace关键字定义和访问命名空间的技术性探讨
C++中使用namespace关键字定义和访问命名空间的技术性探讨
16 3
|
24天前
|
编译器 C++
C++中执行父类构造方法的技术性探讨
C++中执行父类构造方法的技术性探讨
11 1