文章目录
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块的代码试图解决异常问题或者终止程序。
- 可以让一个类包含一个嵌套的异常类。