首页IT科技c++模板教程(c++学习笔记——模板和IO(二))

c++模板教程(c++学习笔记——模板和IO(二))

时间2025-05-05 15:04:04分类IT科技浏览4300
导读: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版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!

展开全文READ MORE
window10安装方法(官方win10安装教程)