引言
在现代 C++ 中,大多数情况下报告和处理逻辑错误和运行时错误的首选方式都是使用异常。 当堆栈可能包含一些在可检测错误的函数和具有上下文来了解如何解决该错误的函数间的函数调用时,更是如此。 异常提供正式且定义完善的代码检测错误方法,以在调用堆栈上向上传递信息。 程序错误通常分为两类:第一类为由编程错误导致的逻辑错误,例如,“索引超出范围”错误。第二类为超出程序员控制的运行时错误,例如“网络服务不可用”错误。 在 C 风格编程和 COM 中,通过返回代表错误代码或特殊函数状态代码的值或者通过设置调用方可在每次调用函数后检索以确定是否已报告错误的全局变量管理错误报告。 例如,COM 编程使用 HRESULT 返回值来将错误传递给调用方,而 Win32 API 使用 GetLastError 函数来检索由调用堆栈报告的上一个错误。 在这两种情况下,由调用方识别代码并相应地作出回应。 如果调用方未显式处理错误代码,则程序可能在不发出任何警告的情况下崩溃,或者继续使用错误数据执行并产生错误结果。
现代 C++ 更趋向于使用异常的原因如下:
– 异常强制调用代码识别错误条件并对其进行处理。 未经处理的异常会中止程序执行。
– 异常跳转到能够处理错误的调用堆栈的点。 中间函数可导致异常传播。 它们不必与其他层相协调。
– 在引发异常后,异常堆栈展开结构基于显式定义的规则销毁处于范围内的所有对象。
– 异常将介于检测错误和处理错误之间的代码完全分离。
异常捕捉
异常捕捉的方式主要是通过[try,catch]完成,语法格式如下:
ry{ //function operation } catch(exception resource) { //handle exception }
异常处理
异常处理的方式有两种:
– 用户自定义(User-defined)
– C++11提供异常处理
用户自定义
// HandleExcepetion.cpp : 定义控制台应用程序的入口点。
#include using namespace std; const int num = 5; class MyClass { public: MyClass(); MyClass(int size = num); int & operator[](int off); const int & operator[](int off)const; int Getsize()const{ return size; }; class Wrong{};//定义错误异常 class offset{ // 偏移异常类的定义 public: offset(int Size) :size(Size){}; ~offset(){}; virtual int get(){ return size; }; virtual void show(){ cout << "抛出offset异常" << endl; cout << "异常的下标为" << size << "出错" << endl; } protected: int size; }; class Big :public offset//偏移较大 { public: Big(int Size) :offset(Size){}; virtual void show() { cout << "抛出Big异常" << endl; cout << "异常的下标为" << offset::size << "出错" << endl; } }; class Nav :public offset//无偏移 { public: Nav(int size) :offset(size){}; virtual void show() { cout << "抛出Nav异常" << endl; cout << "异常的下标为" < } }; class small//偏移很小 { public: small(int Size) :size(Size){} virtual int get(){ return size; }; virtual void show(){ cout << "抛出small异常" << endl; cout << "异常的下标" << size << "出错"<<endl; } ~small(){}; protected: int size; private: }; class zero : public small//偏移为0 { public: zero(int size) :small(size){} virtual void show() { cout << "抛出zero 异常" << endl; cout << "异常的下标" << small::size << "出错" << endl; } }; ~MyClass(){ delete[]p; } private: int size; int *p; }; MyClass::MyClass(int size /* = num */) :size(size) { cout << "Myclass Constuctor operation" << endl; if (size==0) { throw small(size); } if (size<10) { throw small(size); } if (size>1000) { throw Big(size); } if (size<1) { throw Nav(size); } p = new int[size]; for (int i = 0; i++;i<size) { p[i] = 0; } } int & MyClass:: operator[](int off) { if (off<0&&off<Getsize()) { return p[off]; } throw Wrong(); return p[0]; } const int & MyClass::operator[](int off)const { if (off { return p[off]; } throw Wrong(); return p[0]; } int _tmain(int argc, _TCHAR* argv[]) { try { MyClass me(0);//试图捕获偏移为0异常 } catch (MyClass::small &zero) { zero.show(); } catch (MyClass::Big &big) { big.show(); } catch (MyClass::Nav &nav) { nav.show(); } return 0; }
解释:在上述的代码中定义了错误异常类Wrong和偏移异常类offset,针对偏移异常又细分了几种情况即偏移较大Big、便宜较小small等。代码对对每一个类定义了相应的虚函数、构造函数等。
系统定义
系统定义的异常类在:
`#include<stdexcept>`中包含的异常类定义如下:
#include #include #include using namespace std; class MyClass { public: void MyFunc(char c) { if(c < numeric_limits::max()) throw invalid_argument("MyFunc argument too large."); //... } }; int main() { try { MyFunc(256); //cause an exception to throw } catch(invalid_argument& e) { cerr << e.what() << endl; return -1; } //... return 0; }
结论
异常和断言是检测程序中运行时错误的两个不同的机制。 如果您的所有代码都是正确的,那么对于开发中绝不应为 true 的条件,请使用断言进行测试。 无法使用异常来处理此类错误,因为错误表明需要确认代码的某部分,但不代表在运行时程序需从某状况中恢复。 断言停止执行该语句,以便您可以检查调试器中的程序状态;异常继续从第一个正确 catch 处理程序开始执行。 使用异常检查运行时可能会出现的错误条件(即使您的代码是正确的),例如“未找到文件”或“在内存不足”。您可能希望从这些情况恢复,即使恢复仅向日志输出一条消息就结束程序。 请始终使用异常检查公共函数的参数。 即使您的功能没有错误,但也可能不能完全控制用户传递给它的参数。[table id=1 /]