C++ day33 异常(二)exception类

catch块的顺序

上一篇文章说了对于一组有继承关系的异常类,catch块的顺序。现在说下其他情况下的顺序。

完全不知道会引发什么异常

确实有这种情况存在。比如你写了一个函数,调用了别人写的一个函数,你不清楚别人的这个函数可能会引发什么异常。

这时候,可以在catch的括号中写省略号,表示任何异常类型,就可以捕捉任何类型的异常。

catch (```)
{
	···
}

知道一部分可能引发的异常

可以把自己知道的可能的异常写在前面,然后把省略号表示的任何类型的异常写在最后,就像switch语句的default语句一样嘚。当然前面那些异常如果有继承关系,也要按照祖先类在后孩子类在前的顺序。

try{
	duper();
}
catch (bad_3 & be){
···
}
catch (bad_2 & be){
···
}
catch (bad_1 & be){
···
}
catch(```){//catch whatever is left
···
}

如果catch参数是类的对象,不是类的引用,会怎样?

即按值传递。当然啦,传回来的只是throw的对象的副本,因为按值传递嘛。

那么捕获到的就是对象的副本,不再是对象的副本的引用,这时候,如果catch参数是基类对象,则所有派生类的对象仍然会被捕捉,但是却不再可以派生,所以catch块内只能使用虚方法的基类版本,而没办法使用派生类自己的版本

引用真是有用,原来按引用传递才能维持继承关系。

exception类

C++的定义了很多从exception类派生来的异常类。这些类的名称指出了他们报告的错误类型。

stdexcept头文件

这个头文件定义了从exception类公有派生来的logic_error类和runtime_error类。这两个类成为了两大派生类系列的基类。即异常类大致分为两大派系,分别由logic_error类和runtime_error类派生出去。

如果这两个类以及他们派生的类还是没办法解决你的问题,你可以自己从logic_error类和runtime_error类中派生一个异常类,自己来写。从他俩中派生是为了保证你的异常也在继承层次中,catch块的顺序好放。
在这里插入图片描述在这里插入图片描述

可以看到,这几个类的构造函数都接受一个string引用参数。

logic_error类:描述典型的逻辑错误,这类错误可以通过编程修复

logic_error类派生出来的异常类有:

  • domain_error类:domain,是定义域的意思。range表示值域。
    当数学函数的参数不在此函数的定义域内时,引发这个异常。比如std::asin()反正弦函数,定义域是-1到+1,如果输入参数不在这个范围,就引发异常。

我试了试

cout << std::asin(2);

应该是引发了异常,异常被捕捉到,catch块的操作是返回非数值。只不过没有打印有关异常的任何信息而已,实际上asin函数的库版本一定是用了异常处理的。

nan
  • invalid_argument类: 传递的参数无效,比如函数只要0或1,你传2,就会引发。
  • length_error类:没有足够空间就会引发。比如,string类的append方法,合并两个string对象时,得到的串的长度如果超出了最大允许长度(?),就会引发。
  • out_of_bounds类:指示索引错误。比如数组下标不对。

这些错误,平时也多多少少都见过。

在这里插入图片描述

runtime_error类:这类异常是无法避免的问题

runtime_error类派生出来的异常类有:

  • overflow_error:当整型和浮点型的计算结果超出自己类型能表示的最大范围时候,就会引发上溢异常。
  • underflow_error类:只发生在浮点数的计算中,整型不会遇到这个问题。计算结果比浮点类型能够表示的最小非零值还要小的时候,就会引发。
  • range_error类:计算结果没有上溢也没下溢,但是不在函数允许的范围内,就引发。

在这里插入图片描述

bad_alloc异常类:也从exception类公有派生而来,处理new分配内存失败的错误

C语言malloc函数,以及C++以前的new运算符,分配内存失败返回一个空指针,后来C++改用异常处理,C还是返回空指针。

示例

//main.cpp
#include <iostream>
#include <string>
#include <new>
#include <cstdlib> //exit()函数
struct Big{
	double stuff[20000];
};
int main()
{
	Big * p;
	try{
		p = new Big[10000];//1.6GB
		std::cout << "Got past the new request: \n";
	}
	catch (std::bad_alloc & ba){
		std::cout << "Caught the exception!\n";
		std::cout << ba.what() << std::endl;
		exit(EXIT_FAILURE);
	}
	std::cout << "Memory successfully allocated!\n";
	p[0].stuff[0] = 4;
	std::cout << p[0].stuff[0] << std::endl;
delete [] p;

	return 0;
}
Got past the new request:
Memory successfully allocated!
4
double stuff[20000];
Caught the exception!
std::bad_array_new_length

Process returned 1 (0x1)   execution time : 0.337 s
Press any key to continue.

我和书上返回的字符串不一样啊。
在这里插入图片描述

和空指针方案兼容的方法(不太好使?)

书上还说,为了和以前的空指针方案兼容,毕竟有很多代码已经那么写了。于是C++标准提供了一个开关,让用户来选择到底用哪一种方案。

代码:

//main.cpp
#include <iostream>
#include <string>
#include <new>
#include <cstdlib> //exit()函数
struct Big{
	double stuff[20000];
};
int main()
{
	Big * p;
	p = new (std::nothrow) Big[50000];//8GB
	if (p == 0)
	{
		std::cout << "Could not allocate memory!\n";
		exit(EXIT_FAILURE);
	}

	std::cout << "Memory successfully allocated!\n";
	p[0].stuff[0] = 4;
	std::cout << p[0].stuff[0] << std::endl;
    delete [] p;

	return 0;
}

核心是p = new (std::nothrow) Big[50000];//8GB

但是我跑出来的结果,还是抛出异常了,而且由于没有catch块捕捉,所以程序只好调用了terminate函数,然后终止了函数。

大概是我的编译器不支持这个???

terminate called after throwing an instance of 'std::bad_array_new_length'
  what():  std::bad_array_new_length

Process returned 3 (0x3)   execution time : 0.708 s
Press any key to continue.

异常和继承的密切邂逅:在类中嵌套异常类声明

像上面所说的,标准C++库把异常和继承结合的方式是:从exception类派生出两个,再从这两个类中继续派生。

除了这种方式,还有别的方法把异常和继承联系在一起: 在类定义中嵌套异常类声明,以组合异常。 把这种嵌套类作为基类,派生出新的类。

示例

天哪,,,我都不知道自己在敲什么了,头皮发麻,眼睛发干发涩,脖子僵硬酸痛,听着音乐振奋精神。。。写了快一个小时

在Sales类的公有部分定义了一个嵌套类用作异常类,继承了logic_error类,

//sales.h -- exceptions and inheritance
#ifndef _SALES_H_
#define _SALES_H_
#include <stdexcept>
#include <string>

class Sales
{
public:
	enum {MONTHS = 12};
	//嵌套类 ,在公有部分嵌套,派生类和外界可访问
	class bad_index : public std::logic_error
	{
	private:
		int bi;
	public:
		explicit bad_index(int ix,
			const std::string & s = "Index error in Sales Object\n");
		int bi_val() const {return bi;}
		virtual ~bad_index()throw(){}//虚析构函数
	};
	explicit Sales(int yy = 0);
	Sales(int yy, const double * gr, int n);
	virtual ~Sales(){}
	int Year() const {return year;}
	virtual double operator[](int i)const;
	virtual double & operator[](int i);
private:
	double gross[MONTHS];
	int year;//变量不可和方法名相同
};
class LabeledSales : public Sales
{
public:
	class nbad_index : public Sales::bad_index
	{
	private:
		std::string lbl;
	public:
		nbad_index(const std::string & lb, int ix,
			const std::string & s = "Index error in LabledSales object\n");
		const std::string & label_val() const {return lbl;}
		virtual ~nbad_index()throw(){}
	};
	explicit LabeledSales(const std::string & lb = "none",
		int yy = 0);
	LabeledSales(const std::string & lb, int yy, const double * gr, int n);
	virtual ~LabeledSales(){}
	const std::string & Label() const {return label;}
	virtual double operator[](int i) const;
	virtual double & operator[](int i);
private:
	std::string label;
};
#endif
//sales.cpp -- methods for class Sales
#include <iostream>
#include "sales.h"
using std::string;

Sales::bad_index::bad_index(int ix, const string & s)
	: std::logic_error(s), bi(ix)
{}

Sales::Sales(int yy)
{
	year = yy;
	int i;
	for (i=0;i<MONTHS;++i)//初始化为0
		gross[i] = 0;
}

Sales::Sales(int yy, const double * gr, int n)
{
	year = yy;
	int lim = (n < MONTHS) ? n : MONTHS;
	int i;
	for (i=0;i<lim;++i)
		gross[i] = gr[i];
	for (;i<MONTHS;++i)//剩下几个月的初始化为0
		gross[i] = 0;
}

double Sales::operator[](int i) const
{
	if (i < 0 || i > MONTHS - 1)
		throw bad_index(i);
	return gross[i];
}

double & Sales::operator[](int i)
{
	if (i < 0 || i > MONTHS - 1)
		throw bad_index(i);
	return gross[i];
}

LabeledSales::nbad_index::nbad_index(const string & lb, int ix,
const string & s):Sales::bad_index(ix, s)
{
	lbl = lb;
}

LabeledSales::LabeledSales(const string & lb, int yy):Sales(yy)
{
	label = lb;
}

LabeledSales::LabeledSales(const string & lb, int yy,
const double * gr, int n):Sales(yy, gr, n)
{
	label = lb;
}

double LabeledSales::operator[](int i) const
{
	if (i < 0 || i >= MONTHS)
		throw nbad_index(Label(), i);
	return Sales::operator[](i);
}

double & LabeledSales::operator[](int i)
{
	if (i < 0 || i >= MONTHS)
		throw nbad_index(Label(), i);
	return Sales::operator[](i);
}
//use_sales.cpp -- nested exceptions嵌套异常
#include <iostream>
#include "sales.h"

int main()
{
	using std::cout;
	using std::cin;
	using std::endl;

	double vals1[12] =
	{
		1220, 1100, 1122, 2212, 1232, 2334,
		2884, 2393, 3302, 2922, 3002, 3544
	};

	double vals2[12] =
	{
		12, 11, 22, 21, 32, 34,
		28, 29, 33, 29, 32, 35
	};

	Sales sales1(2011, vals1, 12);
	LabeledSales sales2("Blogstar", 2012, vals2, 12);

	cout << "First try block:\n";
	try{
		int i;
		cout << "Year = " << sales1.Year() << endl;
		for (i=0;i<12;++i)
		{
			cout << sales1[i] << ' ';
			if (i % 6 == 5)
				cout << endl;
		}
		cout << "Year = " << sales2.Year() << endl;
		cout << "Label = " << sales2.Label() << endl;
		for (i=0;i<=12;++i)
		{
			cout << sales2[i] << ' ';
			if (i % 6 == 5)
				cout << endl;
		}
		cout << "End of try block 1.\n";
	}
	catch (LabeledSales::nbad_index & bad){
		cout << bad.what();
		cout << "Company: " << bad.label_val() << endl;
		cout << "bad_index: " << bad.bi_val() << endl;

	}
	catch (Sales::bad_index & bad){
		cout << bad.what();
		cout << "bad_index: " << bad.bi_val() << endl;
	}
	cout << "\nNext try block:\n";
	try
	{
		sales2[2] = 37.5;
		sales1[20] = 23345;
		cout << "End of try block 2.\n";
	}
	catch (LabeledSales::nbad_index & bad){
		cout << bad.what();
		cout << "Company: " << bad.label_val() << endl;
		cout << "bad index: " << bad.bi_val() << endl;
	}
	catch (Sales::bad_index &bad){
		cout << bad.what();
		cout << "bad index: " << bad.bi_val() << endl;
	}
	cout << "done\n";
	return 0;
}
First try block:
Year = 2011
1220 1100 1122 2212 1232 2334
2884 2393 3302 2922 3002 3544
Year = 2012
Label = Blogstar
12 11 22 21 32 34
28 29 33 29 32 35
Index error in LabledSales object
Company: Blogstar
bad_index: 12

Next try block:
Index error in Sales Object
bad index: 20
done

总结

  • 引发异常会终止当前正在执行的函数,程序的控制权会移交给匹配的catch块。但是直接或者间接引发异常的函数必须要位于try块中,才可以捕获到异常。catch块的代码试图解决异常问题或者终止程序。
  • 可以让一个类包含一个嵌套的异常类。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值