-
传统C中的错误处理
1)通过返回值表示错误
优点:函数调用路径中所有栈对象都可以被右花括号正确的析构,不会内存泄漏.
缺点:错误处理流程比较复杂,需要逐层返回值判断,代码臃肿.#include <iostream> #include <cstdio> using namespace std; class A{ public: A(void){ cout << "A的构造函数" << endl; } ~A(void){ cout << "A的构造析构" << endl; } }; int func3(){ A a; FILE* file = fopen("xx.txt","r"); if(file==NULL){ cout << "文件打开失败" << endl; return -1; } cout << "func3正常执行" << endl; fclose(file); return 0; } int func2(){ A a; if(func3()==-1){ return -1; } cout << "func2正常执行" << endl; return 0; } int func1(){ A a; if(func2()==-1){ return -1; } cout << "func1正常执行" << endl; return 0; } int main(){ if(func1()==-1){ return -1; } cout << "main正常执行" << endl; return 0; }
2)通过远程跳转处理错误
优点:不需要逐层返回值判断,一步到位处理错误处理,代码精炼
缺点:函数调用路径中的栈对象失去被析构机会,有内存泄漏的风险.#include <iostream> #include <cstdio> #include <csetjmp>//远程跳转 jmp_buf g_env; using namespace std; class A{ public: A(void){ cout << "A的构造函数" << endl; } ~A(void){ cout << "A的构造析构" << endl; } }; int func3(){ A a; FILE* file = fopen("xx.txt","r"); if(file==NULL){ longjmp(g_env,-1); } cout << "func3正常执行" << endl; fclose(file); return 0; } int func2(){ A a; func3(); cout << "func2正常执行" << endl; return 0; } int func1(){ A a; func2(); cout << "func1正常执行" << endl; return 0; } int main(){ if(setjmp(g_env) == -1){ cout << "文件打开失败" << endl; return -1; } func1(); cout << "main正常执行" << endl; return 0; }
-
C++异常机制
1)异常抛出
throw 异常对象;
注:异常对象可以是基本类型也可以是类类型对象.2)异常检测和捕获
try{
可能引发异常语句;
…
}
catch(异常类型1){
针对异常类型1的处理
}
catch(异常类型2){
针对异常类型2的处理
}
…
注:catch子句根据异常对象类型自上而下顺序匹配,而不是最优匹配,因此要把对子类类型异常捕获语句要写在前面,否则将会被基类类型的异常捕获语句提前的截获。#include <iostream> #include <cstdio> using namespace std; class FileError{ public: FileError(const string& file,int line) :m_file(file),m_line(line){ cout << "出错位置:" << m_file << "," << m_line << endl; } private: string m_file; int m_line; }; class A{ public: A(void){ cout << "A的构造函数" << endl; } ~A(void){ cout << "A的构造析构" << endl; } }; int func3(){ A a; FILE* file = fopen("xx.txt","r"); if(file==NULL){ //预定义的宏: //__FILE__:当前文件名字的字符串 //__LINE__:当前位置行号(int) throw FileError(__FILE__,__LINE__); //throw -1;//抛出异常 } cout << "func3正常执行" << endl; fclose(file); return 0; } int func2(){ A a; func3(); cout << "func2正常执行" << endl; return 0; } int func1(){ A a; func2(); cout << "func1正常执行" << endl; return 0; } int main(){ try{ func1(); cout << "main正常执行" << endl; } catch(int ex){ if(ex == -1){ cout << "文件打开失败" << endl; return - 1; } } catch(FileError& ex){ cout << "File Open Error" << endl; return -1; } return 0; }
-
函数的异常说明
1)函数的异常说明用于告诉函数调用者,在该函数执行期间可能会抛出的异常类型.
返回类型 函数名(形参表) throw(异常类型表) {函数体}
eg:
//说明func函数执行时可能会抛出FileError或int类型异常
void func(void) throw(FileError,int){…}
2)函数的异常说明是一种承诺,表示该函数所抛出的异常类型不会超出说明范围。但如果抛出异常说明以外的其它类型,则无法被该函数的调用者正常捕获,而是继续向上抛出,最终被系统捕获,导致进程终止
3)函数异常说明的两种极端形式
–》不写函数异常说明,表示可以抛出任何异常
–》空异常说明,“throw()”,表示不会抛出任何异常
4)如果函数的声明和定义分开写,要保证异常说明类型一致,但是顺序无所谓
5)如果基类中的虚函数带有异常说明,那么该函数在子类中的覆盖版本不能说明比基类抛出更多异常,否则将会因为“放松throw限定”而导致编译失败 -
标准异常类(exception)
class exception{ public: exception() throw() { } virtual ~exception() throw() ; /*Returns a C-style character string describing the general cause of the current error. */ virtual const char* what() const throw(); };
利用多态和标准异常类实现更加便捷的异常处理:#include <iostream> #include <exception> using namespace std; class FileError:public exception{ public: const char* what(void) const throw(){ cout << "针对文件错误处理" << endl; return "FileError"; } }; class MermoryError:public exception{ public: const char* what(void) const throw(){ cout << "针对内存错误处理" << endl; return "MermoryError"; } }; void func(void){ //throw FileError(); throw MermoryError(); } int main(void){ try{ func(); } catch(exception& ex){ cout << ex.what() << endl; } return 0; }
-
构造函数和析构函数中的异常
1)构造函数可以抛出异常,但是对象将会被不完整构造,这样的对象其析构函数不再会被自动调用执行,因此在构造函数抛出异常之前,需要手动释放之前分配的动态资源.#include <iostream> using namespace std; class A{ public: A(void){ cout << "动态资源分配" << endl; if("error"){ cout << "动态资源释放" << endl; throw -1; } cout << "构造函数正常结束" << endl; } ~A(void){ cout << "动态资源释放" << endl; } }; int main(void){ try{ A a; } catch(int ex){ cout << "捕获到异常:" << ex << endl; } return 0; }
2)析构函数不要抛出异常:一方面是抛了异常没人处理(显示调用析构可用try语句处理,但显示调用情况比较复杂,比如,double free等等),另一方面,如果在执行过程中发生了异常,析构也抛出异常,多个异常会导致进程终止,代码如下:
#include <iostream> using namespace std; class A{ public: void func(void){ throw -2; } ~A(void){ throw -1; } }; int main(void){ try{ A a; a.func();//throw -2 }//throw -1 catch(int ex){ cout << "捕获到异常:" << ex << endl; } return 0; }