详解C++异常处理使用方法

1.异常处理

1.1什么是异常处理

异常是指再程序正常运行的过程中的发生的反常行为,比如说遇到意外的用户输入情况。

1.2抛出异常

当程序的某部分监测到一个它无法处理的问题时,它首先应该发出某种信号,以表明它遇到了无法解决的问题,并且它只负责报告异常,至于程序会如何处理,不是它的任务。

1.3异常处理

一般而言,如果程序中有监测异常的代码,通常就会有处理异常的代码。比如说引发程序异常的问题是输入无效,那么处理异常的代码就可以是提示用户重新输入真确的数据。

2.C++实现异常处理的3个关键字

2.1 throw、try、catch

  1. 用“throw表达式”来抛出异常,用法是“throw + 表达式 ;”。如:
  • throw “the input is error.”;//抛出const char* 的异常类型
  • throw 1;//抛出int型的异常类型
  • throw runtime_error(“Data must refer to ISBN”); //抛出runtime_error类类型的异常类型,其中runtime_error是一个标准的异常处理类
  1. 用“try语句块”来处理异常
    在这里插入图片描述
  • try语句块中的program-statements放置正常运行的程序,同时它也是可能出问题的程序。一旦有异常发生,就用throw表达式来抛出异常。这里注意,try中声明的变量在外部无法访问,catch也不行
  • catch的语句块(handler-statements)就是放置异常处理代码的地方。这里提个问题:catch是怎样匹配throw抛出的异常的呢?答案就是catch通过括号()中的异常声明(exception-declaration)来匹配。当throw抛出的是int型的异常类型,则用int声明一个变量来匹配。如下:当你输入一个<0的数时,throw -1,即抛出一个int型的异常,接着catch就能捕获到int型异常,并匹配异常声明(exception-declaration),接着就执行catch(int i)下的语句块,并且此时i的值就是-1。
int a;
	try
	{
		cin >> a;

		if (a< 0)
			throw -1;
	}
	catch (int i){
		cerr << "input < 0,please input again:" << endl;
		cin >> a;
	}
	catch (const char* c){
		cerr << "const char* error" << endl;
	}

具体的代码例子会在后面给出。

2.2寻找处理代码的过程(匹配catch的过程)

在复杂的系统中,程序在遇到抛出异常的代码之前,执行路径可能已经经过多个try语句块,也就是说try语句块有嵌套。那么,在throw抛出异常后,寻找catch异常处理的代码是怎样的一个过程呢?答案就是逐层向外寻找。即本层throw出的异常,若在本层没找到对应的catch异常处理,则在调用它的层里继续匹配寻找。

如下:getInput()函数在用户输入<0的整型数据时,会throw -1,但是在getInput()函数内并没有处理异常的代码,所以在调用它的函数继续匹配,而后就匹配到了main中catch(int i),则执行catch(int i)下的语句块。

int a;
void getInput()
{
	cin >> a;

	if (a< 0)
		throw -1;
}
int main()
{
	try{
		getInput();
	}
	catch (int i){
		cerr << "input < 0,please input again:" << endl;
		cin >> a;
	}
	catch (const char* c){
		cerr << "const char* error" << endl;
	}
}

注意当上述的匹配过程中最后都没能匹配到任何catch字句,程序会转到terminate的标准库函数,一般来说执行了这个函数,程序就非正常退出了。

对于没有任何try语句块的异常——即只用throw抛出了异常,却没有try catch来捕获异常,和上述情况类似,调用terminate后程序非正常退出。

下面举一个例子来说明异常处理的使用:

#include <iostream>
#include <exception>
using namespace std;

double calQuotient(double &_a, double &_b)
{
	if (_b == 0) throw "输入的分母是0。"; //抛出const char* 类型的异常
	return _a / _b;
}

int main()
{
	double numerator, denominator;//numerator:分子。denominator:分母
	while (cin >> numerator >> denominator){//用户输入分子分母
		try{
			double quotient = calQuotient(numerator, denominator);//calQuotient()函数是计算商的函数
			std::cout << numerator<<"/"<<denominator<<" = "<<quotient << std::endl;
			break;//得到了正确得商,退出while
		}
		catch (const char* s){//分母为0的异常处理代码
			std::cout << s 			//此时s的值就是throw的表达式的值
				<<"是否在输一次?Enter y or n"<< std::endl;
			char c;
			cin >> c;
			if (c == 'n')
				break;//用户输入n,退出while循环
		}
	}

	system("pause");
	return 0;
}

上述代码的功能是用户输入两个double类型的数,分别为分子和分母,然后调用calQuotient函数计算商。如果分母不为0,打印商;若分母为0,calQuotient函数抛出异常,并在main中用catch处理异常。异常处理的过程是询问用户是否重新输入,若不重新输入,直接退出。

代码运行结果如下:
在这里插入图片描述

3.C++定义的标准异常类

C++ 提供了一系列标准的异常,它们是以父子类层次结构组织起来的,如下所示:
在这里插入图片描述
下面是对异常类的说明:参考至:https://www.runoob.com/cplusplus/cpp-exceptions-handling.html

在这里插入图片描述

3.1使用C++的标准异常类实例

例子1:
以前面给出的实例为例,我们做如下修改:

  • if (_b == 0) throw "输入的分母是0。";修改为
    if (_b == 0) throw runtime_error("输入的分母是0。");

  • catch(const char* s)修改为catch(runtime_error err)
    std::cout << s修改为std::cout << err.what()

最后的结果和修改前一样,只不过使用的异常类。

这里注意异常类只定义了一个名为what的成员函数,该函数没有任何参数,返回值是一个指向C风格字符串的const char*。该字符串的目的是提供有关异常的一些文本信息。就如"输入的分母是0。"提示一样。对于那些无初始值的异常类型来说,what返回的内容由编译器决定。

例子2:
参考至:http://c.biancheng.net/view/422.html

#include <iostream>
#include <stdexcept>
using namespace std;
int main()
{
	//bad_alloc异常类实例
	try {
		char * p = new char[0x7fffffff];  //无法分配这么多空间,会抛出异常
	}
	catch (bad_alloc e)  {
		cerr << e.what() << endl;
	}

	//out_of_range异常类实例
	string s = "I love China";
	try {
		char c = s.at(100);  //拋出 out_of_range 异常
	}
	catch (out_of_range & e) {
		cerr << e.what() << endl;
	}

	system("pause");
	return 0;
}

在这里插入图片描述
从这个例子可以看出,有些异常是系统自动抛出,并不需要我们自己写抛出异常。

4.如何定义和使用自己的异常类

我们可以通过继承C++标准异常类的方法来定义自己的异常类。
实例:

#include <iostream>
#include <exception>
#include <stdexcept>
using namespace std;

class MyException :public exception
{
public:
	MyException() :c(nullptr){}
	MyException(const char* _c):c(_c){}
	virtual ~MyException(){}

	//覆写what成员函数
	const char* what() throw()
	{
		if (c == nullptr)
			return "MyException() error catch!";
		return c;
	}
private:
	const char * c;
};

int main()
{
	//默认参数-构造一个MyException类
	try{
		throw MyException();
	}
	catch (MyException & myexp){
		cerr << myexp.what() << endl;
	}

	//实际传参-构造一个MyException类
	try{
		throw MyException("Error have been found.");
	}
	catch(MyException & myexp){
		cerr << myexp.what() << endl;
	}

	system("pause");
	return 0;
}

在这里插入图片描述

在上面的实例中,我们覆写了异常类的一个公共成员函数what,从而可以返回一些自己定义的异常的提示文本信息

这里注意:在覆写what成员函数时,我们后面接了一个throw(),它其实是一个异常接口声明列出函数可能抛出的所有异常类型,增加程序的可读性和可维护性

  • void fun() throw(int ,const char*);则fun函数可能抛出int和const char*类型的异常;
  • void fun() throw();表明函数fun不会抛出任何异常。
  • void fun();表明函数可以抛出任何异常。

函数如果拋出了其异常声明列表中没有的异常,在编译时不会引发错误,但在运行时, Dev C++ 编译出来的程序会出错;用 Visual Studio 2013编译出来的程序会提示警告信息,但是程序依旧可以正常运行。

5.总结

  1. try和catch必须成对出现。catch通过()中的异常声明来匹配throw抛出的异常类型(也就是throw后接的表达式类型)
  2. catch的匹配是逐层向外匹配。即当前函数没有catch,则在调用它的函数里找catch
  3. 若throw抛出的异常最终没有匹配到catch或则根本就没有catch,系统调用terminate后退出程序
  4. what()函数是C++异常类的一个方法。返回值为const char *类型
  5. 异常接声明。列出函数可能抛出的所有异常类型,增加程序的可读性和可维护性

以上就是C++异常类的使用方法。有关C++异常类的底层实现机制,本文不做讨论,有兴趣的朋友可以自行查阅相关书籍。

本人水平有限,如有错误,欢迎各位指正。

参考书籍:C++ primer 第5版

  • 6
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值