一,异常的推演
1.函数与异常
平时我们在函数中出现异常情况时通常通过return终止函数并返回一个值,然后在函数上层来获取值并判断是什么异常情况。因为函数是栈结构的,所以return的时候是通过栈结构逐步往上的,不能够跨函数直接抛出,不方便。所以C++推出了异常机制,通过异常机制我们可以轻松的捕获要出现的异常。
2.C++中异常的基本演示
# include<iostream> using namespace std; /* 定义求商函数,如果除数为0,抛出double类型异常 */ double div(double d1, double d2) { if (d2 == 0) { throw d2; } return d1 / d2; } int main() { /* try...catch语句来捕获异常 */ try { double result = div(1.02, 0.0); } catch (double e) { /* C++中的异常严格要求类型匹配,即抛出什么的异常就catch什么类型的异常 */ cout << e << "不能作除数" << endl; } catch (...) { cout << "未知异常" << endl; } return 0; }
3.C++中异常的总结
- 异常的捕捉严格匹配数据类型,不支持类型自动转换,throw的是int类型则catch的必须是int类型否则不会匹配。
- 一般我们在异常最后加入catch(...)这样能够捕获任意异常。
二,自定义异常类
1.异常类
按照面向对象的思维,我们的异常也应该是一个对象,所以在抛出异常的时候,我们通常自定义一个异常类,然后把异常信息放入异常类中,然后在捕捉到异常对象的时候,再调用对象的方法,打印出异常的信息。
2.异常类代码演示
# define _CRT_SECURE_NO_WARNINGS # include<iostream> using namespace std; /* 自定义异常类 */ class MyException { private: char * content; public: /* 异常类构造函数 */ MyException(const char * content) { this->content = new char[strlen(content) + 1]; strcpy(this->content, content); cout << "异常类有参构造函数执行" << endl; } /* 异常类拷贝构造函数 */ MyException(const MyException& me) { this->content = new char[strlen(me.content) + 1]; strcpy(this->content, me.content); cout << "异常类拷贝构造函数执行" << endl; } /* 异常类析构函数 */ ~MyException() { if (this->content != NULL) { delete[] this->content; this->content = NULL; } cout << "异常类的析构函数执行" << endl; } /* 异常类抛出异常信息 */ void toString() { cout << this->content << endl; } }; /* 定义测试异常的函数 */ double divide(double d1, double d2) { if (d2 == 0) { throw MyException("除数不能为0"); //throw new MyException("除数不能为0"); } return d1 / d2; } int main() { try { double result = divide(1, 0); cout << "result = " << result << endl; } catch (MyException e) { // catch的异常是元素类型,执行的是拷贝构造函数,存在两个异常对象,释放两个异常对象,不合理 e.toString(); } catch (MyException * e) { // catch的异常是指针类型,必须我们手动调用delete方法才能调用析构函数,不合理 e->toString(); delete e; } catch (MyException& e) { // catch的是引用类型,是原先抛出的对象,会自动执行析构函数,我们使用引用来接收抛出的异常对象 e.toString(); } catch (...) { cout << "未知异常" << endl; } return 0; }
3.自定义异常类总结
- 抛出异常对象通常用引用的方式来catch这个对象,如果是元素catch异常对象则会执行拷贝构造函数,创建两个重复异常对象并释放两次对象,所以说不合理。如果catch的是指针类型,不会自动调用该对象的析构函数必须我们手动delete,也不符合自动调用的逻辑,所以使用引用的方式来catch异常对象。
三,标准异常类
1.标准异常类
C++中提供了标准的异常类,需要# include<exception>,标准异常类为exception,该类有个what函数,可以打印异常对象的异常信息。该what函数是虚函数,我们需要继承exception类并重写该what函数,在捕捉异常的时候,我们用父类的引用来接收自定义的标准异常类的子类对象(类型兼容性原则),然后打印该异常信息即可。
2.标准异常类演示
# include<iostream> # include<exception> using namespace std; /* 继承自标准异常类 */ class DivException :public exception { private: const char * ptr; public: /* 构造函数接收异常信息 */ DivException(const char * ptr) { this->ptr = ptr; } /* 重写what函数 */ virtual char const * what() const { cout << this->ptr << endl; return ptr; } }; /* 测试异常函数 */ int divi(int a, int b) { if (b == 0) { throw DivException("除数不能为0"); } return a / b; } int main() { try { divi(10, 0); } catch (exception& e) { /* 使用exception引用接收自定义的子类对象 */ e.what(); } catch (...) { cout << "未知异常" << endl; } return 0; }