1、异常
详细见《c++异常分类》
基类exception有一个virtual函数what,返回错误信息(构造函数设定的)。
基类exception的派生类有runtime_error(运行时错误,运行后检测)、logic_error(逻辑错误,运行前检测)和运算符抛出的异常。
运算符抛出的异常包括:bad_alloc(new抛出),bad_cast(dynamic_cast抛出),bad_typeid(typeid抛出),bad_exception
注意:
(1)如果一个函数抛出列表中包含bad_exception,那么如果一个意料之外的异常发生时,函数unexcpected将抛出bad_exception而不是结束程序,或调用set_unexpected指出的函数。
(2)自定义异常可以不继承exception,所以catch(exception)不能保证捕获所有异常
(3)catch(...)可以捕获所有异常,但是由于没有参数所以没法查找该异常。
2、throw
除了抛出异常,也可以抛出表达式值或int值,如throw x > 5 或 throw 5
重新抛出异常如下:
catch(xxxx){
throw;
}
注意:在catch语句外有throw;语句会调用terminate函数,会导致程序结束。
注意:带有常见错误的函数应返回0或者NULL,而不是抛出异常。通过检查返回值确定调用是否成功。
3、异常说明
列举一系列函数可以抛出的异常,如:
void do(..) throw (ExceptionA, ExceptionB, ...){...}
注意:
(1)抛出一个异常说明不允许的异常,会调用unexpected函数。
(2)没有提供异常说明的函数可以抛出任何异常。在函数设置一个空异常说明throw()
表示该函数没有抛出任何异常,如果试图抛出异常,调用unexpected函数。
4、处理意料之外异常
unexpected函数会调用函数set_unexpected注册的函数,如果没有注册会默认调用terminate函数;
terminate函数会调用set_terminate注册的函数,如果没有默认调用abort函数。
调用terminate函数的四种情况:
(1)对抛出的异常,异常机制找不到匹配的catch块
(2)析构函数试图在堆栈展开时抛出一个异常
(3)在没有异常要处理是试图重新抛出异常(即第二条中的注意事项)
(4)调用函数unexpected将默认调用函数terminate
注意:set_unexpected和set_terminate会分别返回一个指向函数unexpected和terminate最后一次调用的函数的指针,如果第一次调用则返回0
注意:set_unexpected和set_terminate接收void返回值且没有参数的函数指针作为参数。
注意:如果自定的终止函数的最后行为并不是退出程序,那么这个函数执行完后会调abort终止程序。
5、堆栈展开
当一个异常被抛出但没有在一个特定的域内被捕获时,该函数调用堆栈就会展开,并试图在下一个外部try..catch内捕获。
展开 函数调用的堆栈,此函数中所有局部变量被销毁。
6、构造、析构函数和异常
构造函数可以抛出异常,异常抛出前会调用析构函数。
如果由于堆栈展开而调用析构函数抛出了一个异常,terminate函数被调用(terminate函数调用四个情况之一)。
7、处理new失败
编译器不同,处理方式不同,有三种:默认返回0;抛出异常(已包含头文件);默认抛出bad_alloc
set_new_handler可以注册一个new失败后的异常处理器。参数是一个没有参数且返回值为空的函数的指针。
注意:一旦注册在set_new_handler注册了,那么new失败时不会抛出bad_alloc,将错误堆栈推给new处理器来处理。
c++标准明确指出new处理器要完成以下任务的一个:
(1)释放其他动态分配内存,并返回运算符new来尝试再次分配内存
(2)抛出bad_alloc型异常
(3)调用函数abort或exit来结束程序
8、auto_ptr和动态分配内存
如果一个异常发生在成功分配内存后,delete语句前,那么发生内存泄漏。auto_ptr可以处理这种情况。
一个auto_ptr对象维护了指向动态分配内存的指针,当一个auto_ptr对象析构函数被调用,它将对其指针的数据成员执行delete。
由于auto_ptr类模板提供了重载运算符"*"和"->",auto_ptr对象可以作为一般指针变量使用。如:
auto_ptr<A> ptr(new A(...)); ptr->do(...) //do函数是A类的成员函数 复制代码
注意:
(1)auto_ptr不能指向数组和标准容器类。
(2)auto_ptr能通过它的重载赋值运算符和拷贝构造函数来传递动态内存所有权
9、setjump和longjump
能够指定从一个深度嵌套调用中立即跳转到一个错误处理器(不必层层抛出)。但很危险,因为没有调用为自动对象建立的析构函数。
请注明出处。