c++模板教程(c++学习笔记——模板和IO(二))
导读:C++异常 前言:...
C++异常
前言:
异常处理就是处理程序中的错误 。所谓错误是指在程序运行的过程中发生的一些异常事件(如:除0溢出 ,数组下标越界 ,所要读取的文件不存在 ,空指针 ,内存不足等等)
在对C语言的学习中 ,我们常常对错误的处理围绕着两种方法:一种是使用整型的返回值标识错误;二是使用errno宏(可以简单的理解为一个全局整型变量)去记录错误 。
但这两种方法最大的缺陷就是会出现不一致问题 。例如有些函数返回1表示成功 ,返回0标识错误;而有些函数返回0标识成功 ,返回非0表示错误 。还有一个缺陷是函数的返回值只有一个 ,你通过函数的返回值表示错误代码 ,那么函数就不能返回其他的值 。C++相比C语言在异常处理上有哪些优势呢?
我们可先看看下面异常语法的代码再进行总结 。
//异常的基本语法 int func(int a, int b) { if (b == 0) { //第二步: throw 10; //抛出一个int类型的异常 } return a / b; } void test() { int a = 10; int b = 0; //把有可能出现异常的代码块放到try中 try { func(a, b); //第一步 } catch (int) //第三步 { cout << "接收一个int类型的异常" << endl; } }回到上面的问题
函数的返回值可以忽略,但异常不可忽略 。如果程序出现异常 ,但是没有被捕获 ,程序就会终止,这多少会促使程序员开发出来的程序更健壮一点 。而如果使用C语言的error宏或者函数返回值 ,调用者都有可能忘记检查 ,从而没有对错误进行处理,结果造成程序莫名其面的终止或出现错误的结果 。 整型返回值没有任何语义信息。而异常却包含语义信息 ,有时你从类名就能够体现出来 。 整型返回值缺乏相关的上下文信息 。异常作为一个类 ,可以拥有自己的成员 ,这些成员就可以传递足够的信息。 异常处理可以在调用跳级 。这是一个代码编写时的问题:假设在有多个函数的调用栈中出现了某个错误 ,使用整型返回码要求你在每一级函数中都要进行处理 。而使用异常处理的栈展开机制 ,只需要在一处进行处理就可以了 ,不需要每级函数都处理。异常具有严格的类型匹配
异常机制和函数机制互不干涉,但是捕捉方式是通过严格类型匹配 。
int func(int a, int b) { if (b == 0) { throw 20.2f; } return a / b; } void test() { int a = 10; int b = 0; try { func(a, b); } catch (double s) { cout << "接收一个double类型的异常" << endl; } catch (char) { cout << "接收一个char类型的异常" << endl; } catch (...) //接收其他类型的异常 { cout << "接收一个其他类型的异常" << endl; } }栈解旋(unwinding)
异常被抛出后 ,从进入try块起 ,到异常被抛掷前 ,这期间在栈上构造的所有对象,都会被自动析构 。析构的顺序与构造的顺序相反 ,这一过程称为栈的解旋(unwinding).
class Maker { public: Maker() { cout << "Maker的构造" << endl; } Maker(const Maker& m) { cout << "Maker的拷贝构造" << endl; } ~Maker() { cout << "Maker的析构" << endl; } }; void func() { //在抛出异常的函数中 ,如果抛出异常之后,但函数没有结束 ,这时 ,栈上申请的对象都会被释放 //这就叫栈解旋 Maker m; throw m;//这个m是Maker m拷贝构造的 cout << "func函数结束" << endl; } void test() { try { func(); cout << "func()代码后" << endl; } catch (Maker) { cout << "接收一个Maker类型的异常" << endl; } }运行结果:
异常接口声明
为了加强程序的可读性,可以在函数声明中列出可能抛出异常的所有类型 ,例如:void func() throw(A,B,C);这个函数func能够且只能抛出类型A,B,C及其子类型的异常 。 如果在函数声明中没有包含异常接口声明 ,则此函数可以抛任何类型的异常 ,例如:void func() 一个不抛任何类型异常的函数可声明为:void func() throw() 如果一个函数抛出了它的异常接口声明所不允许抛出的异常,unexcepted函数会被调用 ,该函数默认行为调用terminate函数中断程序 。 void func() throw(int, char) //只允许抛出 int 或者 char异常 { throw 10; //抛出一个double类型的异常 } void test() { try { func(); } catch (int) { cout << "抛出一个int型的异常"; } catch (...) { cout << "抛出一个其他型的异常"; } }运行结果:
异常变量周期
产生三个对象
class Maker { public: Maker() { cout << "Maker的构造" << endl; } Maker(const Maker& m) { cout << "Maker的拷贝构造" << endl; } ~Maker() { cout << "Maker的析构" << endl; } }; //产生三个对象 void func1() { Maker m;//第一个对象 ,在异常接收前被释放 throw m;//第二个对象 ,是第一个对象拷贝过来的 } void test01() { try { func1(); } catch (Maker m1)//第三个对象 ,是第二个对象拷贝过来的 { cout << "接收一个Maker类型的异常" << endl; //第二个和第三个对象在catch结束时释放 } }产生两个对象
void func2() { //第一个对象 throw Maker();//匿名对象 } void test02() { try { func2(); } catch (Maker m1)//第二个对象 { cout << "接收一个Maker类型的异常" << endl; //第一个和第二个对象在catch结束时释放 } }产生一个对象
void func3() //常用这种方式 { throw Maker();//匿名对象 } void test03() { try { func3(); } catch (Maker& m1) { cout << "接收一个Maker类型的异常" << endl; } }异常的多态使用
//异常的基类 class Father { public: virtual void printM() { } }; //1 、有继承 class SonNULL :public Father { public: virtual void printM() //2 、重写父类的虚函数 { cout << "空指针的异常" << endl; } }; class SonOut :public Father { public: virtual void printM() { cout << "越位溢出" << endl; } }; void func(int a,int b) { if (a == 0) { throw SonNULL(); } if (b == 0) { throw SonOut(); } } void test() { int a = 0; int b = 10; try { func(a, b); } catch (Father& f) //3、父类引用指向子类对象 { f.printM(); } }创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!