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

本文涉及的产品
实时数仓Hologres,5000CU*H 100GB 3个月
智能开放搜索 OpenSearch行业算法版,1GB 20LCU 1个月
检索分析服务 Elasticsearch 版,2核4GB开发者规格 1个月
简介: 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 }
相关文章
|
3月前
|
C++
C++ 语言异常处理实战:在编程潮流中坚守稳定,开启代码可靠之旅
【8月更文挑战第22天】C++的异常处理机制是确保程序稳定的关键特性。它允许程序在遇到错误时优雅地响应而非直接崩溃。通过`throw`抛出异常,并用`catch`捕获处理,可使程序控制流跳转至错误处理代码。例如,在进行除法运算或文件读取时,若发生除数为零或文件无法打开等错误,则可通过抛出异常并在调用处捕获来妥善处理这些情况。恰当使用异常处理能显著提升程序的健壮性和维护性。
74 2
|
2月前
|
存储 算法 C++
C++提高篇:泛型编程和STL技术详解,探讨C++更深层的使用
文章详细探讨了C++中的泛型编程与STL技术,重点讲解了如何使用模板来创建通用的函数和类,以及模板在提高代码复用性和灵活性方面的作用。
50 2
C++提高篇:泛型编程和STL技术详解,探讨C++更深层的使用
|
5月前
|
存储 分布式数据库 API
技术好文:VisualC++查看文件被哪个进程占用
技术好文:VisualC++查看文件被哪个进程占用
|
3月前
|
人工智能 Anolis
聚焦C++20 最新标准!技术 Workshop 精彩亮点一览 | 2024 龙蜥大会
多场技术 Workshop、多位领域专家亲自授课,分享独家洞察与宝贵经验。
|
3月前
|
算法 C# 开发工具
《黑神话:悟空》背后的编程语言揭秘——超越C++的多元技术融合
【8月更文挑战第27天】在游戏开发领域,一款游戏的成功往往离不开其背后强大的技术支持和编程语言的精妙运用。《黑神话:悟空》作为备受瞩目的国产单机动作游戏,其开发过程不仅涉及了多种编程语言,更是一次技术创新的集中展现。然而,当我们深入探讨其开发语言时,会发现它并非仅依赖于单一的C++,而是融合了多种编程语言的优势,共同铸就了这款游戏的辉煌。
254 0
|
5月前
|
C++
C++一分钟之—异常处理try-catch
【6月更文挑战第22天】C++异常处理机制,借助`try`、`catch`、`throw`管理错误,优雅处理异常,防止程序崩溃。`try`包围可能出错的代码,`catch`捕获异常,`throw`引发异常。基本结构是:`try-catch`块中,未捕获的异常将向上抛出。多`catch`块可按顺序捕获不同类型的异常。易错点包括忽视异常传播、不精确的`catch`和资源未清理。通过精确捕获、RAII技术和适当的异常策略,提升代码健壮性和效率。
41 1
|
5月前
|
C++
C++核心技术要点《友元函数,友元类》
C++核心技术要点《友元函数,友元类》
54 3
|
5月前
|
C++ 索引
C++核心技术要点《运算符重载》
C++核心技术要点《运算符重载》
53 2
|
5月前
|
程序员 编译器 C++
探索C++语言宝库:解锁基础知识与实用技能(类型变量+条件循环+函数模块+OOP+异常处理)
探索C++语言宝库:解锁基础知识与实用技能(类型变量+条件循环+函数模块+OOP+异常处理)
47 0
|
5月前
|
SQL 人工智能 算法
技术心得记录:模板函数函数模板FunctionTemplate(C++Primer
技术心得记录:模板函数函数模板FunctionTemplate(C++Primer