C++异常处理

一、C++异常处理基本语法

1.C++通过throw语句和try...catch语句实现对异常的处理

throw  表达式;

该语句抛出一个异常。异常是一个表达式,类型可以是基本类型,也可以是类。

2.try...catch语句的语法如下:

try {
    执行语句组(包含throw语句)
}
catch(异常类型) {
    异常处理代码
}
...
catch(...) {       //捕获所有异常
    异常处理代码
}

try...catch语句的执行过程是:

  • 执行 try 块中的语句,如果执行的过程中没有异常拋出,那么执行完跳过所有的catch块
  • 如果 try 块执行的过程中拋出了异常try块中throw之后的语句不执行,拋出异常后立即跳转到第一个与拋出的异常类型匹配的 catch 块中执行(称作异常被该 catch 块“捕获”),执行完后再跳转到最后一个 catch 块后面继续执行。

3.抛出异常时使用字符串描述异常信息的两种方式:

  • 执行try块中的语句,如果执行的过程中没有异常抛出,那么执行完后会跳过所有的catch块然后执行之后的语句。
  • 如果try块中抛出了异常那么try块中throw之后的语句不会执行,然后根据throw语句中抛出异常的类型,跳转到第一个类型匹配的catch块中执行,执行完后再跳转到最后一个catch块后的语句继续执行
int main()
{
	double m, n;
	cin >> m >> n;
	try {
		if (n == 0)
			throw string("The denominator is 0");
		cout << m / n << endl;
	}
	catch (string s) {
		cout << s << endl;
	}
	return 0;
}

输出结果:

#include<iostream>
#include<string>
using namespace std;
int main()
{
	double m, n;
	cin >> m >> n;
	try {
		if (n == 0)
			throw "The denominator is 0";
		cout << m / n << endl;
	}
	catch (const char *msg) {
		cout << msg << endl;
	}
	return 0;
}

输出:

4.能够处理任何异常的语句catch(...)  { 异常处理代码 }

(由于可以捕获所有类型的异常,它之后的catch块实际上就不起作用,因此不要将它写在其他的catch块之后)

int main()
{
	double m, n;
	cin >> m >> n;
	try {
		if (n == 0)
			throw string("The denominator is 0");
		else if (m == 0)
			throw - 1;
		cout << m / n << endl;
	}
	
	catch (string s) {
		cout << s << endl;
	}
	catch (...)      //捕获所有类型的异常
	{
		cout << "catch(...)" << endl;
	}
	
	return 0;
}

输出结果:

二、异常的再抛出

  • 如果一个函数在执行过程中抛出的异常在本函数就被catch块捕获并处理,那么该异常就不会抛给这个函数的调用者(或者成为上一层函数);
  • 如果异常在本函数中没有被处理,那么就会被抛上一层函数
#include <iostream>
#include <string>
using namespace std;

double Devide(double x, double y)
{
	if (y == 0)
		throw string("devided by zero");
	cout << "in Devide" << endl;
	return x / y;
}
int CountTax(int salary)
{
	try {
		if (salary < 0)
			throw - 1;
		cout << "counting tax" << endl;
	}
	catch (int) {
		cout << "salary < 0" << endl;
	}
	cout << "end of CountTax function" << endl;
	return salary * 0.15;
}
int main()
{
	double f = 1.2;
	try {
		CountTax(-1);
		f = Devide(3, 0);
		cout << "end of try block" << endl;
	}
	
	catch (CException e) {
		cout << e.msg << endl;
	}
	catch (string s){
		cout << s << endl;
	}
	cout << "f = " << f << endl;
	return 0;
}

输出:

CountTax函数抛出异常后自行处理,这个异常就不会被继续抛给调用者,即main函数。因此main函数的try块中CountTax之后的语句 f=Devision(2,0) 还能正常执行

Devide函数抛出了异常却不处理该异常就会被抛给Devide函数的调用者,即main函数。抛出异常后,devide函数立即结束,之后的 cout << "end of try block" << endl;也不会执行,函数也不会返回值,所以后面 f 的值未被改变

catch块中异常的再抛出

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

int CountTax(int salary)
{
	try {
		if (salary < 0)
			throw string("zero salary");
		cout << "counting tax" << endl;

	}
	catch (string s) {
		cout << "CountTax error : " << s << endl;
		throw;                           //继续抛出捕获的异常,后面的语句不会执行
	}
	cout << "tax counted" << endl;       //不执行
	return salary * 0.15;
}
int main()
{
	double f = 1.2;
	try {
		CountTax(-1);
		cout << "end of try block" << endl;
	}
	catch (string s) {
		cout << s << endl;
	}
	cout << "finished" << endl;
	return 0;
}

三、C++标准异常类

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

                                         

ad_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,则会拋出此异常。

2) bad_cast

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

#include <iostream>
#include <stdexcept>
using namespace std;
class Base
{
    virtual void func() {}
};
class Derived : public Base
{
public:
    void Print() {}
};
void PrintObj(Base & b)
{
    try {
        Derived& rd = dynamic_cast <Derived &>(b);
        //此转换若不安全,会拋出 bad_cast 异常
        rd.Print();
    }
    catch (bad_cast & e) {
        cerr << e.what() << endl;
    }
}
int main()
{
    Base b;
    PrintObj(b);
    return 0;
}

输出:

3) bad_alloc

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

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

输出:

4) out_of_range

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

int main()
{
	vector<int> v(10);
	try {
		//v[100] = 100;   使用下标编译不通过
		v.at(100) = 100;  //拋出 out_of_range 异常
	}
	catch (out_of_range & e) {
		cerr << e.what() << endl;
	}
	string s = "hello";
	try {
		char c = s.at(100);  //拋出 out_of_range 异常
	}
	catch (out_of_range & e) {
		cerr << e.what() << endl;
	}
	return 0;
}

输出:

如果将v.at(100)换成v[100],将s.at(100)换成s[100],程序就不会引发异常(但会导致程序崩溃)。因为 at 成员函数会检测下标越界并拋出异常,而 operator[] 则不会。operator [] 相比 at 的好处就是不用判断下标是否越界,因此执行速度更快。

 

-----------------------------------------------------------------------------------

主要参考:http://c.biancheng.net/view/422.html

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值