c++的异常

语言本身或标准程序库所抛出的所有异常,都派生自基类exception。所有这些标准异常可分为三组:(1)语言本身支持的异常;(2)逻辑错误;(3)运行时错误。
标准库和用户程序都可以抛出后两种异常,可以从第一类异常派生出自定义异常。
c++的异常_第1张图片

1. 语言本身支持的异常

此类异常用以支撑某些语言特性。
(1) new操作失败,会抛出bad_alloc异常(<new>中定义,new的nothrow版本不抛出异常)。
(2) 执行期间,当一个作用于reference身上的“动态型别转换操作”失败时,dynamic_cast会抛出bad_cast异常(<typeinfo>中定义)。
(3) 执行期型别辨识(RTTI)过程中,如果交给typeid的参数为零或空指针,typeid操作符会抛出bad_typeid异常(<typeinfo>中定义)。
(4) 如果发生非预期的异常(函数抛出异常规格(exception specification)以外的异常),程序会自动调用unexpected(),并抛出bad_exception异常(<exception>中定义)。

这类异常都有三个函数:一个是构造函数,不带参数;一个是析构函数,是virtual的;第三个是virtual const char* what() throw()。这三个函数都不会再抛出异常。

还有一个异常ios_base::failure,它定义在ios_base类内部,当数据流由于错误或者到达文件末尾而发生状态改变时,就可能抛出这个异常。
和上面的那些异常不同的是它的构造函数接受一个const string&类型的参数。

下面两类异常定义于`<stdexcept>`,构造函数都有一个const string&类型的参数。

2. 逻辑错误

这类异常总是派生自logic_error。
(1) domain_error指出专业领域范畴内的错误。
(2) invalid_argument表示无效参数,例如将bitset(array of bits)以char而非0或1进行初始化。
(3) length_error指出某个行为“可能超越了最大极限”,例如对某个字符串附加太多字符。
(4) out_of_range指出参数值“不在预期范围内”,例如在处理容器或string中采用一个错误索引。

3. 运行时错误

这类异常派生自runtime_error,用来指出“不在程序范围内,且不容易回避”的事件。
(1) range_error指出内部计算时发生区间错误。
(2) overflow_error指出算术运算发生上溢位。
(3) underflow_error指出算术运算发生下溢位。

基础类别exception和bad_exception定义于<exception>
bad_alloc定义于<new>
bad_cast和bad_typeid定义于<typeinfo>
ios_base::failure定义于<ios>
其他异常类别定义于<stdexcept>

stdexcept

实际实现代码在std_stdexcept.h中:

class logic_error : public exception
{
    string _M_msg;
public:
    explicit logic_error(const string&  __arg);
    virtual ~logic_error() throw();
    virtual const char* what() const throw();
};

class domain_error : public logic_error
{
public:
    explicit domain_error(const string&  __arg);
};

class invalid_argument : public logic_error
{
public:
    explicit invalid_argument(const string&  __arg);
};

class length_error : public logic_error
{
public:
    explicit length_error(const string&  __arg);
};

class out_of_range : public logic_error
{
public:
    explicit out_of_range(const string&  __arg);
};


class runtime_error : public exception
{
    string _M_msg;
public:
    explicit runtime_error(const string&  __arg);
    virtual ~runtime_error() throw();
    virtual const char* what() const throw();
};

class range_error : public runtime_error
{
public:
    explicit range_error(const string&  __arg);
};

class overflow_error : public runtime_error
{
public:
    explicit overflow_error(const string&  __arg);
};

class underflow_error : public runtime_error
{
public:
    explicit underflow_error(const string&  __arg);
};

exception

源代码(稍有改动):

// 标准库可能抛出的所有异常的基类
class exception
{
public:
    exception() throw() { }
    virtual ~exception() throw();
    // 返回描述当前错误的字符串, 派生类应覆写此函数
    virtual const char* what() const throw();
};


/** * 如果发生非预期的异常(函数抛出异常规格(exception specification)以外的异常), 程序会自动调用unexpected, 并抛出bad_exception异常 */
class bad_exception : public exception
{
public:
    bad_exception() throw() { }
    virtual ~bad_exception() throw();
    virtual const char* what() const throw();
};
// 参考terminate的说明
typedef void (*unexpected_handler)();
unexpected_handler set_unexpected(unexpected_handler) throw();
// 默认调用terminate, 但可以通过set_unexpected()更改其行为
void unexpected();


/** * 当一个异常没有catch块捕获时(一层一层throw, throw到main也没有catch处理它), 或出现无法处理的情况, 系统将自动调用终结函数 * 系统提供了一个默认的终结函数terminate(), terminate()会调用abort()退出程序 * 可以通过set_terminate()来设置新的终结函数, 终结函数类型必须是terminate_handler类型的 * terminate()也可以被用户调用 */
typedef void (*terminate_handler)();
terminate_handler set_terminate(terminate_handler) throw();
void terminate() throw();


// 当一个异常已经被抛出, 但在相应的处理程序中异常的初始化还未完成时, 此函数返回true; 其他情况返回false
// 当此函数返回true时抛出其他异常将导致terminate()被调用
bool uncaught_exception() throw();

cplusplus.com提供了一个例子:

// bad_exception example
#include <iostream> // std::cerr
#include <exception> // std::bad_exception, std::set_unexpected

void myunexpected() {
  std::cerr << "unexpected handler called\n";
  throw;
}

void myfunction() throw (int, std::bad_exception) {
  throw 'x'; // throws char (not in exception-specification)
}

int main(void) {
  std::set_unexpected(myunexpected);
  try {
    myfunction();
  }
  catch(int) { std::cerr << "caught int\n"; }
  catch(std::bad_exception be) { std::cerr << "caught bad_exception\n"; }
  catch(...) { std::cerr << "caught some other exception\n"; }
  return 0;
}

输出是:
unexpected handler called
caught bad_exception

构造函数和析构函数与异常

如果对象构造失败的话,就不应该调用析构函数。如果构造失败,应该抛出异常,这样就不会调用析构函数,由异常处理系统负责资源的释放。
如果构造成功,肯定会有响应的析构会被调用。
析构函数是不允许也不应该抛异常的,析构函数隐式的带了一个throw(),如果在析构里抛异常,编译通过,运行会出错。

拓展阅读:
c++标准异常类别
http://www.cnblogs.com/zhuyf87/archive/2012/12/29/2839378.html
bad_exception
http://www.cplusplus.com/reference/exception/bad_exception/

你可能感兴趣的:(异常)