- 六、异常
- 6.1 传统错误处理(如 C语言)
- 6.1.1 通过函数返回值处理异常
- 6.1.2 通过远程跳转处理异常
- 6.2 C++的异常处理
- 6.2.1 对传统错误处理的改造
- 6.3 函数的异常说明
- 6.4 标准异常类
六、异常
异常是指程序运行期间发生的不正常情况,如 new 无法获得所需内存、数组下标越界、运算溢出、除0错误、无效参数以及打开文件不存在灯。异常处理就是指对程序执行过程中产生的异常进行适当的处理,避免程序出现丢失数据或破坏系统运行等灾难性后果。
6.1 传统错误处理(如 C语言)
6.1.1 通过函数返回值处理异常
#include <iostream>
#include <cstdio>
using namespace std;class A{
public:A(void) {cout << "A () " << endl;}~A(void) {cout << "~A ()" << endl;}
};int fun3(){FILE* fp = fopen("./1.text", "r");if(fp == NULL)return -1;cout << "open 1.text successed" << endl;fclose(fp);return 0;
}int fun2(){A b;fun3();
}int fun1(){A a;fun2();
}
int main (void) {A a;fun1();return 0;
}
//输出结果
myubuntu@ubuntu:~/lv19/cplusplus/dy07$ ./a.out
A ()
A ()
~A ()
~A ()
优点:函数调用路径中栈对象得到正确析构,不存在内存泄漏
缺点:错误流程处理复杂,代码臃肿
6.1.2 通过远程跳转处理异常
#include <iostream>
#include <cstdio>
#include <csetjmp>
using namespace std;jmp_buf env;class A{
public:A(void) {cout << "A () " << endl;}~A(void) {cout << "~A ()" << endl;}
};int fun3(){FILE* fp = fopen("./1.text", "r");if(fp == NULL)longjmp(env, -1); //跳转到setjmp位置执行cout << "open 1.text successed" << endl;fclose(fp);return 0;
}int fun2(){A b;fun3();
}int fun1(){A a;fun2();
}
int main (void) {if(setjmp(env) == 0){ //保存当前栈的快照fun1();} else {return -1;}return 0;
}
//输出结果
myubuntu@ubuntu:~/lv19/cplusplus/dy07$ ./a.out
A ()
A ()
优点:不需要逐层判断,一步到位,代码精炼
缺点:函数调用路径中的栈对象失去析构机会,存在内存泄漏风险
6.2 C++的异常处理
对传统错误处理做出了改进:发扬优点,避免缺点。
C++引入了3个用于异常处理的关键字:try, throw, catch。try用于检测可能发生的异常,throw用于抛出异常,catch用于捕获并处理由throw抛出的异常。try-thow-catch构造了C++异常处理的基本结构,形式如下:
try{...if err1 throw xx1; //如果是错误1 就抛出xx1异常...if err2 throw xx2;//如果是错误2 就抛出xx2异常...if errn throw xxn;//如果是错误n 就抛出xxn异常
}
catch(type1 arg){...}
catch(type2 arg){...}
catch(typen arg){...}
6.2.1 对传统错误处理的改造
#include <iostream>
#include <cstdio>
//#include <csetjmp>
using namespace std;//jmp_buf env;class A{
public:A(void) {cout << "A () " << endl;}~A(void) {cout << "~A ()" << endl;}
};class FileErr{
private:string filename;int line;
public:FileErr(const string& name, int num){filename = name;line = num;}friend ostream& operator<<(ostream& os, FileErr& fe);
};ostream& operator<<(ostream& os, FileErr& fe) { //输出重载os << fe.filename << ": " << fe.line << ": " << "file open failed" << endl;return os;
}int fun3(){FILE* fp = fopen("1.text", "r");if(fp == NULL) {throw FileErr(__FILE__, __LINE__); //抛出异常//throw -1; //抛出异常}cout << "open 1.text successed" << endl;fclose(fp);return 0;
}int fun2(){A b;fun3();
}int fun1(){A a;fun2();
}
int main (void) {try{fun1();}catch(int err) {if(err == -1){cout << "handle file open failed" << endl;return -1;}}catch(FileErr fe){ //按类类型返回cout << fe << endl;}return 0;
}
注意:catch在进行数据异常类型匹配时,不会进行数据类型的默认转换,只有与异常类型精确匹配的catch块才会被执行。
6.3 函数的异常说明
当一个函数声明中不带任何异常描述时,它可以抛出任何异常。
C++允许限制函数能够抛出的异常类型,限制方法时在函数声明后面添加一个throw参数表,在其中指定函数可以抛出的异常类型。
int fun(int, char) throw(int, char);
函数fun被限定只允许抛出 int 和 char 类型的异常,当 fun 函数抛出其他类型的异常时,程序将被异常终止。
如果函数不允许抛出任何异常,只需要指定throw限制表为不包括任何类型的空表。
int fun(int, char) throw();
#include <iostream>
#include <cstdio>
#include <csetjmp>
using namespace std;class FileError{};
class MemoryError{};void func(void) throw(FileError, MemoryError){ //func() 函数里面只能抛出 FileError 和 MemoryError 类型的异常//throw FileError();//throw MemoryError();throw -1; //如果抛别的类型异常 就会终止改程序}
int main (void) {try{func();}catch(FileError& er){cout << "FileError err" << endl;}catch(MemoryError& er){cout << "MemoryError err" << endl;}catch(int err){cout << "int err" << endl;}return 0;
}
//输出结果
myubuntu@ubuntu:~/lv19/cplusplus/dy07$ ./a.out
terminate called after throwing an instance of 'int'
Aborted (core dumped)
6.4 标准异常类
#include <iostream>
#include <cstdio>
#include <csetjmp>
using namespace std;class FileError : public exception{
public:virtual const char * what() const throw(){cout << "handle file failed" << endl;return "FileError";}
};
class MemoryError : public exception{
public:virtual const char * what() const throw() {cout << "handle memory failed" << endl;return "MemoryError";}
};void func(void) throw(FileError, MemoryError){ //func() 函数里面只能抛出 FileError 和 MemoryError 类型的异常throw FileError();//throw MemoryError();//throw -1; //如果抛别的类型异常 就会终止改程序}
int main (void) {try{func();}catch(exception& er){/** 这里就用到多态了,多态通过指针或引用指向的那个对象 就调用那个对象中的成员函数* 这样一改造,就不用写很多的catch捕获函数了 写一个就够了* */cout << er.what() << endl;}return 0;
}
//输出结果
myubuntu@ubuntu:~/lv19/cplusplus/dy07$ ./a.out
handle file failed
FileError