c++中的异常

64 篇文章 0 订阅

异常处理

  • 常见的异常:除0溢出,数组下标越界,所要读取的文件不存在,空指针,内存不足等等。
  • c++的异常一旦抛出,如果不捕获,则程序直接退出。
  • c语言通过返回值判断,但有缺陷:1.容易忽略。2.容易和正常值混淆。

abort()函数

  • 向标准错误流(cerr使用的错误流)发送消息:abnormal program termination(程序异常终止),然后终止程序。
  • 也可以使用exit()。

异常机制

  • 抛出异常:throw xxx;
  • 将可能有异常的代码放在try{}中,然后用catch(抛出的异常类型){} 接收抛出的异常。
  • 可以用catch(…){} 接收任何类型的异常。
  • 例子:
int myDiv01(int a,int b)
{
	if(b==0)
		throw 0;//抛出异常
	return a/b;
}
void test02( ) 
{
	try{
		int ret = myDiv01(10, -10) ;
		cout<<"ret = "<<ret<<endl;
	}
	catch(int e)//只不过抛出的异常是int类型
	{
		cout<<"捕获到int类型异常e = "<<e<<end1 ;
	}
}
  • 抛出指定类型的异常:
//只能抛出int char 异常
void testFunc02() throw(int,char)
{
	throw 3.14f ;
}
/ /函数不抛出任何异常
void testFunc03() throw()
{
	throw 10;
}

返回错误码

栈解旋(unwinding)

  • 异常被抛出后,从进入try块起,到异常被抛出前,这期间在栈上构造的所有对象都会被自动析构。
void test03( )
{
	try{
		Person ob1("00_德玛") ;
		Person ob2("01_小炮") ;
		Person ob3("02_小法");
		Person ob4("03_提莫") ; .
		throw 10;
	}
	catch(int)
	{
		cout<<"捕获到int异常"<<endl;
	}
	cout<<"其他工作"<<end1 ;
  • ob1,ob2,ob3,ob4都被析构。

异常的生命周期

在这里插入图片描述

  • 可以通过抛出匿名对象达到相同的效果(推荐):
void test05( )
{
	try{
		throw MyException() ;
	}
	catch (MyException &e)
	{
		cout<<"捕获到MyException &"<<endl ;
	}
}

在这里插入图片描述

标准异常

  • exception是所有异常的基类,往下的都是它的子类:
    在这里插入图片描述

在这里插入图片描述

  • 对于使用new导致的内存分配问题,C++的最新处理方式是让new引发bad_alloc异常。头文件new包含bad_alloc类的声明,它是从exception类公有派生而来的。但在以前,当无法分配请求的内存量时,new返回一个空指针。
  • 空指针和new:很多代码都是在new在失败时返回空指针时编写的。为处理new的变化,有些编译器提供了一个标记(开关),让用户选择所需的行为。当前,C++标准提供了一种在失败时返回空指针的new,其用法如下:
int * pi = new (std::nothrow) int;
int * pa = new (std::nowthrow) int[500];

在这里插入图片描述
在这里插入图片描述

  • domain_error;

  • invalid_argument;

  • length_error;

  • out_of_bounds。

  • 每个类独有一个类似于logic_error的构造函数,让您能够提供一个供方法what()返回的字符串。

  • 数学函数有定义域(domain)和值域(range)。定义域由参数的可能取值组成,值域由函数可能的返回值组成。例如,正弦函数的定义域为负无穷大到正无穷大,因为任何实数都有正弦值;但正弦函数的值域为−1到+1,因为它们分别是最大和最小正弦值。另一方面,反正弦函数的定义域为−1到+1,值域为−π到+ π。如果您编写一个函数,该函数将一个参数传递给函数std::sin(),则可以让该函数在参数不在定义域−1到+1之间时引发domain_error异常。

  • 异常invalid_argument指出给函数传递了一个意料外的值。例如,如果函数希望接受一个这样的字符串:其中每个字符要么是‘0’要么是‘1’,则当传递的字符串中包含其他字符时,该函数将引发invalid_argument异常。

  • 异常length_error用于指出没有足够的空间来执行所需的操作。例如,string类的append()方法在合并得到的字符串长度超过最大允许长度时,将引发length_error异常。

  • 异常out_of_bounds通常用于指示索引错误。例如,您可以定义一个类似于数组的类,其operator() [ ]在使用的索引无效时引发out_of_bounds异常。
    在这里插入图片描述

  • 每个类独有一个类似于runtime_error的构造函数,让您能够提供一个供方法what()返回的字符串。

  • 下溢错误(underflow_error)在浮点数计算中。一般而言,存在浮点类型可以表示的最小非零值,计算结果比这个值还小时将导致下溢错误。

  • 整型和浮点型都可能发生上溢错误(overflow_error),当计算结果超过了某种类型能够表示的最大数量级时,将发生上溢错误。

  • 计算结果可能不再函数允许的范围之内,但没有发生上溢或下溢错误,在这种情况下,可以使用range_error异常。

  • 例子:

class Person
{
private :
	int age;
public :
	Person(int age)
	{
		if(age<0|| age > 150)
		throw out_of_range("age无效") ;
		this->age =age;
	}
};
void test06()
{
	try{
		Person ob(200) ;
	}
	catch(exception &e)
	{
		cout<<"捕获到异常"<<e. what()<<endl;
	}
}

terminate函数

  • 未捕获的异常不会立刻终止,程序首先调用terminate()函数,默认情况下,terminate()调用abort(),可以用set_terminate()指定terminate()要调用的函数,这两者都在头文件exception中声明。
void myQuit()
{
    cout << "Terminating due to uncaught exception\n";
    exit(5);
}

set_terminate(myQuit);
  • 如果引发的异常没有被捕获(catch)时,程序调用terminate(),然后再调用myQuit()。

unexpected函数

  • 发生意外异常,程序调用此函数,然后unexpected调用terminate,再默认调用abort(),也可以用set_unexpected()修改要调用的函数,此函数包含在头文件exception中。
  • 总之,要捕获所有异常:
#include <exception>
using namespace std;
然后,设计一个替代函数,将意外异常转换为bad_exception异常,
该函数的原型如下:

void myUnexpected()
{
    throw std::bad_exception(); //or just throw;
}
仅使用throw,而不指定异常将导致重新引发原来的异常。
然而,如果异常规范中包含了这种类型,
则该异常将被bad_exception对象所取代。

接下来在程序的开始位置,将意外异常操作指定为调用该函数:

set_unexpected(myUnexpected);
最后,将bad_exception类型包括在异常规范中,
并添加如下catch块序列:

double Argh(double, double) throw(out_of_bounds, bad_exception);
...
try {
    x = Argh(a, b);
}
catch(out_of_bounds & ex)
{
    ...
}
catch(bad_exception & ex)
{
    ...
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值