10. 异常
基本语法有两种:
- 抛出异常
- 捕获处理。
发生异常后:
throw
抛出,若被捕捉,则跨函数直接跳到匹配的catch()
保护段;- 若无法处理,可以再次
throw
向上抛出; - 若没有处理,则引发中断。
throw
会创建一个异常对象并抛出。
构造函数没有返回值,经常配合异常机制使用。
捕捉是严格按照类型匹配的,匹配程度近乎模板匹配,不允许任何类型转换。
#include<iostream>
using namespace std;
void divide(int a, int b)
{
if (b == 0)
{
throw 1;
}
cout<<a/b<<endl;
}
int main(int argc, char *argv[])
{
try
{
divide(4,2);
divide(1,0);
}
catch(int e)
{
cout<<"divide 0 error."<<endl;
}
catch( ... )
{
cout<<"Unknown exception."<<endl;
}
cin.get();
return 0;
}
/*
2
divide 0 error.
*/
10.1 异常的基本思想
传统的错误处理是基于函数返回值,以栈结构展开的上下函数衔接的程序控制系统;而异常依附于栈结构,可以在栈中跳跃回馈。
异常的引发和异常的处理不必在同一个函数中,这样底层的函数可以着重解决具体问题,而不必过多的考虑异常的处理。上层调用者可以再适当的位置设计对不同类型异常的处理。
10.2 unwinding
栈解旋:从进入try
开始,到异常被抛出前,期间的对象会自动析构。
#include<iostream>
using namespace std;
class MyException:public exception{};
class MyClass
{
public:
int a;
MyClass(int a)
{
this->a = a;
}
~MyClass()
{
cout<<"destruct "<<a<<endl;
}
};
void func() throw(MyException)
{
throw MyException();
}
int main(int argc, char *argv[])
{
try
{
MyClass c1(1),c2(2);
func();
}
catch(MyException &e)
{
cout<<"MyException."<<endl;
}
cin.get();
return 0;
}
/*
destruct 2
destruct 1
MyException.
*/
10.3 异常接口声明
为了加强可读性,在函数声明中列出所有可能抛出的异常。
void func() throw(A,B,C)
,则该函数只能抛出A,B,C及其子类类型的异常。
若没有声明,则可以抛出任何异常。
若抛出没有声明的异常,则会调用包含terminate()
的unexpected()
函数终止程序。
10.4 异常变量的生命周期
刚刚说过,throw
创建异常对象,catch
严格按照类型匹配。
下面是根据返回值进行错误处理。
int func()
{
{
...
return 1;
}
return 0;
}
int main()
{
int ret = func();
if(ret != 0)
{
switch(ret)
{}
}
return 0;
}
下面是异常处理。
void func()
{
throw 1;
}
int main()
{
try
{
func();
}
catch(int e) //若用不到e,可以写为catch(int)
{}
return 0;
}
该例抛出的1位于静态常量区。
下面是抛出异常类型。
void func()
{
throw MyException(); //匿名对象
}
int main()
{
try
{
func();
}
catch(MyException e) //若用不到e,可以写为catch(int)
{}
return 0;
}
抛出的匿名异常类型对象,仍然遵循函数调用规则,调用拷贝构造方法复制给e
。
如果catch(MyException &e)
,则直接使用抛出时的匿名对象。
指针也可以捕捉:throw new MyException; catch(MyException *e){delete e};
当然我们不会这样用.
10.5 继承在异常中的应用
创建一个数组类来演示异常的层次结构如何使用.
#include<iostream>
using namespace std;
class MyArray{
private:
int *p;
int len;
public:
MyArray(int len);
~MyArray();
//int & operator[](int index);
class eSize
{
public:
virtual void printStackTrace() = 0;
};
class eNeg : public eSize
{
public:
eNeg() : eSize(){}
virtual void printStackTrace()
{
cout<<"len < 0"<<endl;
}
};
class eZero : public eSize
{
public:
eZero() : eSize(){}
virtual void printStackTrace()
{
cout<<"len == 0"<<endl;
}
};
};
MyArray::MyArray(int len)
{
if(len < 0)
{
throw eNeg();
}
else if(len == 0)
{
throw eZero();
}
this->len = len;
p = new int[len];
}
MyArray::~MyArray()
{
if(p != NULL)
{
delete [] p;
p = NULL;
len = 0;
}
}
int main(int argc, char *argv[])
{
try
{
MyArray a(-1);
}
catch(MyArray::eNeg e)
{
e.printStackTrace();
}
cin.get();
return 0;
}
/*
len < 0
*/
10.6 标准异常类
标准库抛出的异常都以Exception
为基类。
class exception {
public:
exception () noexcept;
exception (const exception&) noexcept;
exception& operator= (const exception&) noexcept;
virtual ~exception();
virtual const char* what() const noexcept;
}
what()
用于返回错误信息。
// exception::what
#include <iostream> // std::cout
#include <exception> // std::exception
using namespace std;
struct ooops : exception {
const char* what() const noexcept {return "Ooops!\n";}
};
int main () {
try {
throw ooops();
} catch (exception& ex) {
cout << ex.what();
}
return 0;
}
/*
Ooops!
*/
noexcept
是c++ 11加入的关键字,在这之前相当于throw()
,即不会抛出异常.
涉及头文件:<exception>,<stdexcept>,<typeinfo>,<new>,<ios>
标准异常类的继承关系如下,具体左转官网
E x c e p t i o n { l o g i c _ e r r o r { d o m a i n _ e r r o r i n v a l i d _ a r g u m e n t l e n g t h _ e r r o r o u t _ o f r a n g e f u t u r e _ e r r o r b a d _ a l l o c b a d _ c a s b a d _ e x c e p t i o n b a d _ f u n c t i o n c a l l b a d _ t y p e i d b a d _ w e a k p t r i o s _ b a s e : : f a i l u r e r u n t i m e _ e r r o r { r a n g e _ e r r o r o v e r f l o w _ e r r o r u n d e r f l o w _ e r r o r s y s t e m _ e r r o r Exception \begin{cases} logic\_error \begin{cases} domain\_error\\ invalid\_argument\\ length\_error\\ out\_of_range\\ future\_error \end{cases}\\ bad\_alloc\\ bad\_cas\\ bad\_exception\\ bad\_function_call\\ bad\_typeid\\ bad\_weak_ptr\\ ios\_base::failure\\ runtime\_error \begin{cases} range\_error\\ overflow\_error\\ underflow\_error\\ system\_error \end{cases} \end{cases} Exception⎩⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎨⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎧logic_error⎩⎪⎪⎪⎪⎪⎪⎨⎪⎪⎪⎪⎪⎪⎧domain_errorinvalid_argumentlength_errorout_ofrangefuture_errorbad_allocbad_casbad_exceptionbad_functioncallbad_typeidbad_weakptrios_base::failureruntime_error⎩⎪⎪⎪⎨⎪⎪⎪⎧range_erroroverflow_errorunderflow_errorsystem_error