10,异常处理

1,定义:

异常处理就是处理程序中的错误;

2,基本思想:

让一个函数在发现了自己无法处理的错误时,抛出(throw)一个异常,然后它的(直接或间接)调用者可以吹这个问题;------即将问题检测和问题处理分离;

3,异常基本语法

1,抛出异常使用throw;

2,捕获异常使用try{}catch(){};

throw:当问题出现时,程序会抛出一个异常;
catch:在你想要处理问题的地方,通过异常处理程序捕获异常;
try:try块中的代码标识将被激活的特定异常,它后面通常跟着一个或多个catch块;
noexcept:用于声明函数不抛出异常,如果函数抛出了异常,则直接中断,不能被捕获;

3,语法:

try
{
    //保护代码;
}catch(ExceptionName e1)//ExceptionName是设置抛出异常时,数据的类型;
{
    //catch块
}catch(ExceptionName e2)
{
    ...
}
4,捕获异常时的注意事项:

1,catch的匹配过程是找最先匹配的,从上到下;

2,catch的匹配过程中,对类型的要求比较严格,不允许标准算术转换和类类型的转换;

(类类型的转换包括两种:通过构造函数的隐式类型转换,和通过转换操作符的类型转换);

案例:
    void n()
    {
	int res = 0;
	if (res==0)
	{
		/*throw "除数不能为0";*///const char*类型;
		throw 999;//int 类型;
	}
	int nnm = 5 / res;
    }
    try
	{
		n();
	}
	catch (const char* n)
	{
		cout << "捕获到了char*" <<n<< endl;//输出 捕获到了char*除数不能为0
	}
	catch (int m)
	{
		cout << "捕获到了int" <<m<< endl;//输出 捕获到了int999
	}
5,异常之栈解旋

异常被抛出后,从进入try块起,到异常被抛出前,这期间在栈上构造的所有对象,都会被自动析构;

析构的顺序和构造的顺序相反,这一过程称为栈的解旋;

案例:
class Maker
{
public:
	Maker() { cout << "构造函数" << endl; }
	Maker(const Maker& other) { cout << "拷贝构造函数" << endl; }
	~Maker() { cout << "析构函数" << endl; }
};
void fff()
{
	Maker m;//释放
	//int *p=new int[9];//这个不会自动释放
	throw 999;
}
int main()
{
	try
	{
		fff();
	}
	catch (int d)
	{
		//函数中的局部变量会自动释放;
	}
    //输出构造函数 析构函数
    return 0;
}
6,C++标准的异常

C++提供了一系列标准的异常,定义在中,我们可以在程序中使用这些标准的异常,它们是以父子类层次结构组织起来的;

每个类所在的头文件在图下方标识出来:

标准异常类的成员:

1,在上述继承体系中,每个类都有提供了构造函数,复制构造函数,和赋值操作符重载;

2,logic_error类及其子类,runtime_error类及其子类,它们的构造函数是接受一个string类型的形式参数,用于异常信息的描述;

3,所有的异常类都有一个what()方法,返回const char* 类型(C风格字符串)的值,描述异常信息;

说明:

异常 描述
std::exception 该异常是所有标准C++异常的父类;
std::bad_alloc 该异常可以通过new抛出;
std::bad_cast 该异常可以通过dynamic_cast抛出;
std::bad_exception 这在处理C++程序中无法预期的异常时非常有用;
std::bad_typeid 该异常可以通过typeid抛出;
std::logic_error 理论上可以通过读取代码来检测到的异常;
std::domain_error 当使用了一个无效的数学域时,会抛出该异常;
std::invaild_argument 当使用了无效的参数时,会抛出该异常;
std::length_error 当创建了太长的std::string时,会抛出该异常;
std::out_of_range 该异常可以通过方法抛出,例如:std::vector和std::bitset<>::operator[] ();
std::runtime_error 理论上不可以通过读取代码来检测到的异常;
std::overflow_error 当发生数学上溢时,会抛出该异常;
std::range_error 当尝试存储超出范围的值时,会抛出该异常;
std::underflow_error 当发生数学下溢时,会抛出该异常;
7,编写自己的异常类
class out_Range:public exception//继承父类异常
{
    public:
    explicit out_Range(const string&_Msg):exception(_Msg.c_str()){}//第一种方法:
    explicit out_Range(const char*_Msg):exception(_Msg){}//第二种方法:
}
...
void fun()
{
    throw out_Range("越界了");
}
catch(out_Range& e)
{
   cout<<e.what()<<endl; //输出 越界了
}