首页 > 编程语言 >C++异常处理

C++异常处理

时间:2023-06-18 10:44:39浏览次数:57  
标签:处理 C++ catch 拋出 include 异常 throw

需要异常处理的情况

程序运行时常会碰到一些异常情况,例如:

  • 做除法的时候除数为 0;
  • 用户输入年龄时输入了一个负数;
  • 用 new 运算符动态分配空间时,空间不够导致无法分配;
  • 访问数组元素时,下标越界;打开文件读取时,文件不存在。

这些异常情况,如果不能发现并加以处理,很可能会导致程序崩溃。

异常处理的原理

首先要明确:函数A的异常可以在函数A中自己处理掉,也可以留给它的调用者B去处理

try {
    语句组
}
catch(异常类型) {
    异常处理代码
}
...
catch(异常类型) {
    异常处理代码
}

语句组中会书写想要处理的异常的代码或则说何时发生异常的代码,其实语句组中还必须有throw语句,它后面要接一个表达式其值的类型可以是基本类型,也可以是类,类的话通常会输出异常的信息也就是发生了一个什么样的异常。如果说catch像一个函数的话那么异常类型就是形参,throw后面的表达式就是实参,throw抛出的类型会被catch捕获。整套代码有点像switch,如果一个throw表达式被一个catch所捕获,当然它上面也没有catch捕获这个表达式,那么它后面的catch就直接被跳过,然后继续执行正常的程序段。

 1 #include <iostream>
 2 using namespace std;
 3 int main()
 4 {
 5     double m ,n;
 6     cin >> m >> n;
 7     try {
 8         cout << "before dividing." << endl;
 9         if( n == 0)
10             throw -1; //抛出int类型异常
11         else
12             cout << m / n << endl;
13         cout << "after dividing." << endl;
14     }
15     catch(double d) {
16         cout << "catch(double) " << d <<  endl;
17     }
18     catch(int e) {
19         cout << "catch(int) " << e << endl;
20     }
21     cout << "finished" << endl;
22     return 0;
23 }

运行结果如下:

1 9 6
2 before dividing.
3 1.5
4 after dividing.
5 finished

说明当 n 不为 0 时,try 块中不会拋出异常。因此程序在 try 块正常执行完后,越过所有的 catch 块继续执行,catch 块一个也不会执行。

或则这样将有异常出现:

1 9 0↙
2 before dividing.
3 catch(int) -1
4 finished//异常处理完后面的程序正常执行
  • 如果希望不论拋出哪种类型的异常都能捕获,可以编写如下 catch 块:
1 catch(...) {
2     ...
3 }
 1 #include <iostream>
 2 using namespace std;
 3 int main()
 4 {
 5     double m, n;
 6     cin >> m >> n;
 7     try {
 8         cout << "before dividing." << endl;
 9         if (n == 0)
10             throw - 1;  //抛出整型异常
11         else if (m == 0)
12             throw - 1.0;  //拋出 double 型异常
13         else
14             cout << m / n << endl;
15         cout << "after dividing." << endl;
16     }
17     catch (double d) {
18         cout << "catch (double)" << d << endl;
19     }
20     catch (...) {
21         cout << "catch (...)" << endl;
22     }
23     cout << "finished" << endl;
24     return 0;
25 }
1 9 0↙
2 before dividing.
3 catch (...)
4 finished

或则这样:

1 0 6↙
2 before dividing.
3 catch (double) -1
4 finished

当 m 为 0 时,拋出一个 double 类型的异常。虽然catch (double)和catch(…)都能匹配该异常,但是catch(double)是第一个能匹配的 catch 块,因此会执行它,而不会执行catch(…)块,这有点类似于函数重载的最佳匹配。

由于catch(…)能匹配任何类型的异常,它后面的 catch 块实际上就不起作用,因此不要将它写在其他 catch 块前面。

  • 如果没有一个catch能够捕获throw后面的语句的话,也就是没有一个形参可以和实参相匹配的话,那么这个异常就要交给调用它的函数去处理,如果没有函数调用它的话则库函数terminate将被自动调用,其默认是调用abort终止上程序。

    

 1 #include <iostream>
 2 #include <string>
 3 using namespace std;
 4 class CException
 5 {
 6 public:
 7     string msg;
 8     CException(string s) : msg(s) {}
 9 };
10 double Devide(double x, double y)
11 {
12     if (y == 0)
13         throw CException("devided by zero");
14     cout << "in Devide" << endl;
15     return x / y;
16 }
17 int CountTax(int salary)
18 {
19     try {
20         if (salary < 0)
21             throw - 1;
22         cout << "counting tax" << endl;
23     }
24     catch (int) {
25         cout << "salary < 0" << endl;
26     }
27     cout << "tax counted" << endl;
28     return salary * 0.15;
29 }
30 int main()
31 {
32     double f = 1.2;
33     try {
34         CountTax(-1);
35         f = Devide(3, 0);
36         cout << "end of try block" << endl;
37     }
38     catch (CException e) {
39         cout << e.msg << endl;
40     }
41     cout << "f = " << f << endl;
42     cout << "finished" << endl;
43     return 0;
44 }
salary < 0
tax counted
devided by zero
f=1.2
finished
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         
  • 有时,虽然在函数中对异常进行了处理,但是还是希望能够通知调用者,以便让调用者知道发生了异常,从而可以作进一步的处理。在 catch 块中拋出异常可以满足这种需要。例如:
 1 #include <iostream>
 2 #include <string>
 3 using namespace std;
 4 int CountTax(int salary)
 5 {
 6     try {
 7         if( salary < 0 )
 8             throw string("zero salary");
 9         cout << "counting tax" << endl;
10     }
11     catch (string s ) {
12         cout << "CountTax error : " << s << endl;
13         throw; //继续抛出捕获的异常,如果一个catch语句里面还有一个throw,那么它将会把它捕获到的异常再抛给调用它的函数
14     }
15     cout << "tax counted" << endl;
16     return salary * 0.15;
17 }
18 int main()
19 {
20     double f = 1.2;
21     try {
22         CountTax(-1);
23         cout << "end of try block" << endl;
24     }
25     catch(string s) {
26         cout << s << endl;
27     }
28     cout << "finished" << endl;
29     return 0;
30 }
  • 为了增强程序的可读性和可维护性,使程序员在使用一个函数时就能看出这个函数可能会拋出哪些异常,C++ 允许在函数声明和定义时,加上它所能拋出的异常的列表,具体写法如下:
1 //声明
2 void func() throw (int, double, A, B, C);
//定义
void func() throw (int, double, A, B, C){...}

上面的写法表明 func 可能拋出 int 型、double 型以及 A、B、C 三种类型的异常。异常声明列表可以在函数声明时写,也可以在函数定义时写。如果两处都写,则两处应一致。

如果异常声明列表如下编写:

1 void func() throw ();

则说明 func 函数不会拋出任何异常。

一个函数如果不交待能拋出哪些类型的异常,就可以拋出任何类型的异常。

函数如果拋出了其异常声明列表中没有的异常,在编译时不会引发错误,但在运行时, Dev C++ 编译出来的程序会出错;用 Visual Studio 2010 编译出来的程序则不会出错,异常声明列表不起实际作用。

C++标准异常类

C++ 标准库中有一些类代表异常,这些类都是从 exception 类派生而来的。常用的几个异常类如图 1 所示。

 

bad_typeid、bad_cast、bad_alloc、ios_base::failure、out_of_range 都是 exception 类的派生类。C++ 程序在碰到某些异常时,即使程序中没有写 throw 语句,也会自动拋出上述异常类的对象。这些异常类还都有名为 what 的成员函数,返回字符串形式的异常描述信息。使用这些异常类需要包含头文件 stdexcept。

下面分别介绍以上几个异常类。本节程序的输出以 Visual Studio 2010为准,Dev C++ 编译的程序输出有所不同。

1) bad_typeid

使用 typeid运算符时,如果其操作数是一个多态类的指针,而该指针的值为 NULL,则会拋出此异常。

 1 #include <iostream>
 2 
 3 #include <typeinfo>
 4 
 5 
 6 
 7 class Base {
 8 
 9 public:
10 
11     virtual ~Base() {}
12 
13 };
14 
15 
16 
17 class Derived1 : public Base {};
18 
19 class Derived2 : public Base {};
20 
21 
22 
23 int main() {
24 
25     Base* p = nullptr; // 定义一个空指针
26 
27     
28 
29     try {
30 
31         std::cout << typeid(p).name() << std::endl; // 输出 "Null pointer to a class of type 'Base'"
32 
33     } catch (std::exception& e) {
34 
35         std::cerr << e.what() << std::endl; // 输出 "nullptr"
36 
37     }
38 
39     
40 
41     return 0;
42 
43 }

2) bad_cast

在用 dynamic_cast 进行从多态基类对象(或引用)到派生类的引用的强制类型转换时,如果转换是不安全的,则会拋出此异常。程序示例如下:

C++中的dynamic_cast是一种类型转换运算符,它允许在运行时检查一个对象的类型并将其转换为更派生类。当您需要将一个基类对象向下转换为其派生类对象,但不确定该对象是否实际继承自所需的派生类时,可以使用dynamic_cast

 

 1 #include <iostream>
 2 #include <stdexcept>
 3 using namespace std;
 4 class Base
 5 {
 6     virtual void func() {}
 7 };
 8 class Derived : public Base
 9 {
10 public:
11     void Print() {}
12 };
13 void PrintObj(Base & b)
14 {
15     try {
16         Derived & rd = dynamic_cast <Derived &>(b);
17         //此转换若不安全,会拋出 bad_cast 异常,如果是C++标准库中的异常,可以不用throw
18         rd.Print();
19     }
20     catch (bad_cast & e) {
21         cerr << e.what() << endl;
22     }
23 }
24 int main()
25 {
26     Base b;
27     PrintObj(b);
28     return 0;
29 }

 

1 Bad dynamic_cast!

在 PrintObj 函数中,通过 dynamic_cast 检测 b 是否引用的是一个 Derived 对象,如果是,就调用其 Print 成员函数;如果不是,就拋出异常,不会调用 Derived::Print。

3) bad_alloc

在用 new 运算符进行动态内存分配时,如果没有足够的内存,则会引发此异常。程序示例如下:

 1 #include <iostream>
 2 #include <stdexcept>
 3 using namespace std;
 4 int main()
 5 {
 6     try {
 7         char * p = new char[0x7fffffff];  //无法分配这么多空间,会抛出异常
 8     }
 9     catch (bad_alloc & e)  {
10         cerr << e.what() << endl;
11     }
12     return 0;
13 }
bad allocation
ios_base::failure

4) out_of_range

用 vector 或 string 的 at 成员函数根据下标访问元素时,如果下标越界,则会拋出此异常。例如:

 1 #include <iostream>
 2 #include <stdexcept>
 3 #include <vector>
 4 #include <string>
 5 using namespace std;
 6 int main()
 7 {
 8     vector<int> v(10);
 9     try {
10         v.at(100) = 100;  //拋出 out_of_range 异常
11     }
12     catch (out_of_range & e) {
13         cerr << e.what() << endl;
14     }
15     string s = "hello";
16     try {
17         char c = s.at(100);  //拋出 out_of_range 异常
18     }
19     catch (out_of_range & e) {
20         cerr << e.what() << endl;
21     }
22     return 0;
23 }

在C++中,`operator[]`和`at()`都是用于访问容器中的元素的方法。它们的主要区别在于对越界访问的处理方式。

`operator[]`是标准库容器(如vector、list、map等)提供的成员函数,它允许您使用整数索引来访问容器中的元素。如果索引超出了容器的范围,则会抛出`std::out_of_range`异常。因此,在使用`operator[]`时需要特别小心,以避免出现未定义的行为。

相比之下,`at()`是C++11引入的成员函数,它提供了一种更安全的方式来访问容器中的元素。与`operator[]`不同,`at()`不会抛出异常,而是返回一个默认值(通常是容器类型对应的默认构造函数生成的对象),表示访问越界。如果您尝试访问超出范围的元素,则可以使用`at()`来避免抛出异常并提供更好的错误处理机制。因此,如果您需要访问容器中的元素并且可以接受可能的越界访问,则可以使用`operator[]`。但是,如果您需要更安全地访问容器中的元素,并且希望在访问越界时获得更好的错误处理机制,则应该使用`at()`。

 

参考文章

(92条消息) C++异常处理 详解_Yuyao_Xu的博客-CSDN博客

 

void func() throw ();  

标签:处理,C++,catch,拋出,include,异常,throw
From: https://www.cnblogs.com/Sandals-little/p/17488797.html

相关文章

  • 使用Thumbnails进行图片压缩,报“No suitable ImageReader found for source data”异
    先转一次byte数组再处理byte[]bigContent=file.getBytes();Thumbnails.of(newByteArrayInputStream(bigContent)).scale(1f).outputQuality(0.3f).toFile(fileThu);这里fileThu直接使用文件路径比较好......
  • 【React工作记录一百一十三】ant design table项目中遇到的数据处理实例
    前言大家好我是歌谣今天需要进行一个数据处理的问题原始数据到目标数据的处理过程数据处理的过程就是逻辑推理的过程类似一道数学题的解法原始数据格式(本次以两组数据格式为例Rawdata)[{"id":1047,"name":"README.md","manufacture_id":1......
  • 【C++】Effective Modern C++ Key Notes
    [errataveryimportant](https://www.aristeia.com/BookErrata/emc++-errata.html)>Argument,ActualArgument>Parameter,FormalParameter##一类型推导C++98有一套类型推导的规则:用于函数模板的规则。C++11修改了其中的一些规则并增加了两套规则,一套用于auto,一套用于dec......
  • 现代C++学习指南-方向篇
    C++是一门有着四十年历史的语言,先后经历过四次版本大升级(诞生、98、11、17(20),14算小升级)。每次升级都是很多问题和解决方案的取舍。了解这些历史,能更好地帮助我们理清语言的发展脉络。所以接下来我将借它的发展历程,谈一谈我对它的理解,最后给出我认为比较合理的学习路线指南。C++0......
  • 设计 C++ 接口文件的小技巧之 PIMPL
    C++里面有一些惯用法(idioms),如RAII,PIMPL,copy-swap、CRTP、SFINAE等。今天要说的是PIMPL,即PointerToImplementation,指向实现的指针。问题描述在实际的项目中,经常需要定义和第三方/供应商的C++接口。假如有这样一个接口文件:MyInterface.h#include<string>#include<li......
  • 用于提速的一些C++ 编译器的编译选项
    C++Compilerflags在TIO中怎么用?在C++Compilerflags新建几行:-Ofast:这个编译器优化选项启用所有-O3级别的优化,并进一步启用一些可能会破坏标准精度的优化,如忽视IEEE或ISO规定的某些数学准则的优化。这可能会使得程序运行得更快,但也可能会降低精度,因此只有在你可以接......
  • C++面试八股文:了解位运算吗?
    C++面试八股文:了解位运算吗?某日二师兄参加XXX科技公司的C++工程师开发岗位第12面:面试官:了解位运算吗?二师兄:了解一些。(我很熟悉)面试官:请列举以下有哪些位运算?二师兄:按位与(&)、按位或(|)、按位异或(^),按位取反(~)、左移(<<)和右移(>>)。面试官:好的。那你知道位运算有什么优势吗?......
  • [ARM 汇编]进阶篇—异常处理与中断—2.4.2 ARM处理器的异常向量表
    异常向量表简介在ARM架构中,异常向量表是一组固定位置的内存地址,它们包含了处理器在遇到异常时需要跳转到的处理程序的入口地址。每个异常类型都有一个对应的向量地址。当异常发生时,处理器会自动跳转到对应的向量地址,并开始执行异常处理程序。异常向量表的位置ARM处理器的异常向......
  • SpringBoot中跨域问题的处理
    跨越问题产生原因:产生跨域问题的原因是浏览器的同源策略,所谓同源是指:域名,协议,端口相同。如果不同,将会出现跨域问题。一、创建项目我们创建两个项目,一个命名为provider提供服务,一个命名为consumer消费服务,第一个项目端口配置为8080,第二个项目端口配置为8081,然后在provider中提供一个......
  • 《C++》继承
    继承classA:publicB子类:继承方式父类classPhone{public: Phone() { frame="框架"; screen="屏幕"; battery="电池"; }public: stringframe; stringscreen; stringbattery;};classBrand:publicPhone{public: Brand(strin......