




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
RTTI与异常处理8.1RTTI概述
RTTI(Run-TimeTypeIdentification,运行时类型识别),支持程序在运行时,根据基类指针或引用,确定其所指对象的实际类型。RTTI是RAD开发工具(如MFC)的基础。在处理异类容器,以及在实现调试器程序时,都需要在运行时得知对象的类型,而虚函数机制不能满足需求,那么如何确定对象的动态类型呢?C++为此提供的支持是内建的RTTI运算符:dynamic_cast和typeid。8.1RTTI概述dynamic_cast运算符,支持程序在运行时,将基类指针安全转换成派生类指针,或把指向基类的对象安全转换成派生类的引用,所谓安全是指转换成功后所得的指针或引用是一定可用的。typeid运算符,获得指针或引用所指对象运行时的实际类型。8.2dynamic_cast运算符为什么需要dynamic_cast运算符?classEmployee{ //雇员类,基类public:
virtualintsalary();};classCaptain:publicEmployee{ //机长public: intsalary();};classStewardess:publicEmployee{ //空姐public: intsalary();};8.2dynamic_cast运算符另外有一个如下的计算薪水全局函数payroll():intpayroll(Employee*pe){
//使用pe->salary()}假设新需求是在payroll()的计算里,增加一项bonus,与salary()类似,不同类型员工的bonus()是不一样的。为此,可以在Employee类中增加一个bonus()虚成员函数,在计算payroll()时,bonus()与成员函数salary()一起被使用,例如:
8.2dynamic_cast运算符classEmployee{public:
virtualintsalary();
virtualintbonus(); //在基类中增加虚函数};classCaptain:publicEmployee{public: intsalary();};classStewardess:publicEmployee{public: intsalary(); intbonus();};intpayroll(Employee*pe){
//使用pe->salary()和pe->bonus()}8.2dynamic_cast运算符在类层次结构中增加虚函数bonus(),缺点是需要有类Employee、Captain和Stewardess的实现源代码,但如果上述的类层次结构是由第三方库提供的,没有其实现源代码,则无法采用增加基类虚成员函数方式。采用方法:可使用dynamic_cast,为用户在不方便或者不想改变基类的情况下,模拟虚函数机制。具体做法:直接向派生类(如Stewardess类)中增加bonus()成员函数。8.2dynamic_cast运算符classEmployee{public:
virtualintsalary();};classCaptain:publicEmployee{public: intsalary();};classStewardess:publicEmployee{public: intsalary(); intbonus(); //在派生类中增加的非虚成员函数};8.2dynamic_cast运算符在函数payroll()中通过dynamic_cast运算符转换得派生类Stewardess的指针,并用这个指针,调用其增加的非虚成员函数Stewardess::bonus():intpayroll(Employee*pe){Stewardess*pm=dynamic_cast<Stewardess*>(pe);if(pm){ //pm指向Stewardess对象
//用pm调用Stewardess::bonus()}else{ //dynamic_cast失败,pm为0
//使用pe调用Employee的成员函数}}8.2dynamic_cast运算符注意:在使用结果指针之前,必须测试dynamic_cast是否转换成功。例如:intpayroll(Employee*pe){Stewardess*pm=dynamic_cast<Stewardess*>(pe); staticintvariablePay=0;
//错误:在使用pm之前需要测试pm的值 variablePay+=pm->bonus();
//…}8.2dynamic_cast运算符【例8.1】dynamic_cast运算符使用示例。//employee.h#ifndefEMPLOYEE_H //防止employee.h被重复引用#defineEMPLOYEE_HclassEmployee{public:
virtualintsalary();};classCaptain:publicEmployee{public: intsalary();};classStewardess:publicEmployee{public: intsalary(); intbonus(); //增加的非虚成员函数};#endif8.2dynamic_cast运算符//employee.cpp#include"employee.h"intEmployee::salary(){
return0;}intCaptain::salary(){
return20000;}intStewardess::salary(){
return5000;}intStewardess::bonus(){
return2500;}8.2dynamic_cast运算符//mian.cpp#include"employee.h"#include<iostream>usingnamespacestd;intpayroll(Employee*pe){ //全局函数
Stewardess*pm=dynamic_cast<Stewardess*>(pe); if(pm){ //成功,返回派生类指针
//pe->salary()虚函数动态绑定
returnpm->bonus()+pe->salary();
//也可以通过pm静态调用Stewardess类改写后的虚函数pm->salary() //returnpm->bonus()+pm->salary(); } else{ //0
returnpe->salary(); }}8.2dynamic_cast运算符intmain(){Employee*e0=newCaptain();Employee*e1=newStewardess();cout<<payroll(e0)<<endl;cout<<payroll(e1)<<endl;
return0;}运行结果:2000075008.3typeid运算符typeid运算符的操作数可以是表达式或类型名,当表达式是对象指针或引用时,可获得运行时指针或引用所指对象的实际类型,返回一个类型为type_info的对象,type_info类在头文件typeinfo中定义:classtype_info{private: type_info(consttype_info&); type_info&operator=(consttype_info&);public:
virtual~type_info(); intoperator==(consttype_info&)const; intoperator!=(consttype_info&)const; constchar*name()const;};8.3typeid运算符【例8.2】typeid运算符输出操作数类型名示例。//main.cpp#include<typeinfo>#include<iostream>intmain(){ intiobj; std::cout<<typeid(iobj).name()<<std::endl; std::cout<<typeid(8.16).name()<<std::endl;
return0;}运行结果:intdouble8.3typeid运算符【例8.3】typeid运算符的操作数(基类)类型中没有虚函数情况。//main.cpp#include<typeinfo>#include<iostream>classmyBase{/*没有虚函数*/};classmyDerived:publicmyBase{/*没有虚函数*/};intmain(){ myDeriveddobj; myBase*pb=&dobj; std::cout<<typeid(*pb).name()<<std::endl;//输出:myBase return0;}运行结果:classmyBase8.3typeid运算符【例8.4】typeid运算符的操作数(基类)类型中有虚函数示例。//employee.h#ifndefEMPLOYEE_H //防止employee.h被重复引用#defineEMPLOYEE_HclassEmployee{public:
virtualintsalary(){return0;}};classCaptain:publicEmployee{};#endif8.3typeid运算符//main.cpp#include"employee.h"#include<typeinfo>#include<iostream>usingnamespacestd;intmain(){ Employee*pe=newCaptain; //输出:"Captain" cout<<typeid(*pe).name()<<endl;
return0;}运行结果:classCaptain8.4
异常在程序运行时,往往会出现一些不可预期的错误,如除数为0、内存申请失败、类型转换失败,此时,希望能由正常的控制流程之外的代码来处理,比如先释放资源,而不是直接结束程序运行,为此,C++提供异常(exception)处理机制,在C++环境下编程,优先采用的错误处理方式是异常。
首先,异常不会被忽略,如果用户不捕捉(catch)程序中抛出的异常,默认行为是调用abort(),程序被终止(coredump)。其次,异常机制保证了当异常被抛出时,try块内创建的自动变量被撤销。8.5
异常的使用C++中异常有两个部分:异常的抛出,以及异常的处理。当程序出错,C++通过throw表达式抛出异常;在异常出现之后,程序的正常执行被暂停,异常处理机制开始搜寻程序中有能力处理这一异常的catch子句,在异常处理完毕之后,程序从异常处理地点继续向下执行。8.5
异常的使用1.抛出异常voidfoo()throw(exception_1,exception_n){ if(error_1(…)){ //…
throwexception_1; //1 } … if(error_n(…)){ //…
throwexception_n; }}8.5
异常的使用其中,exception_1、exception_n是程序出错时抛出的异常,异常是某种对象,最简单的异常对象可以是一个整数或者字符串,如:throw32;throw"helloexception";8.5
异常的使用2.处理异常voidbar(){
try
{ foo(); //2 }
catch(exception_1e1){ //3 handle_exception_1(e1); }
catch(exception_nen){ handle_exception_n(en); }}函数在一旦抛出异常后就自动终止运行,将运行的权限交给它的上一层调用方。8.5
异常的使用3.trytry块中的语句是可能抛出异常的地方。try总是与catch一同出现,try把一组catch子句与try块内的函数体关联起来,对于一个try块,至少应该有一个catch()子句,如果函数体中的语句抛出了一个异常,则使用函数体后面catch子句来处理这个异常。8.5
异常的使用4.catch在程序中,利用一个或者一串catch子句来捕捉(catch)被抛出的异常对象,catch子句由3部分组成:关键字catch、圆括号内的一个类型或者对象、花括号内的一组语句(用于处理异常),如上例中:catch(exception_nen){ handle_exception(en);}8.5
异常的使用如下为T类型的对象定义一个catch子句(变量t是任意的,与函数形参类似):catch(Tt)catch(constTt)catch(T&t)catch(constT&t)编译器按照catch出现的顺序以及catch指定的参数类型,确定一个异常应该由哪个catch来处理。异常对象会被用来依次和每个catch子句比较类型,如果类型相符,则该catch子句的内容便会执行。
8.5
异常的使用catch不一定要全部捕捉try块中抛出的异常,剩下没有捕捉的可以交给上一级函数处理。在程序中,可以使用一网打尽(catch-all)的方式,使用(…)作为catch的参数,支持捕捉任意类型的异常,示例如下:catch(…){ //捕捉任何类型的异常
log_message(); //处理异常}8.5
异常的使用有时,catch子句可能无法完成异常的完整处理,在处理之后,程序可以重抛异常,以寻求其它catch子句的协助,作进一步的处理:catch(iterator_overflow&iof){ log_message(iof.what_happened());
throw;//重抛异常,使另一个catch子句可以继续处理}重抛时,只需在catch子句中写下throw,throw会将捕捉的异常对象再次抛出,并由另一个类型吻合的catch子句继续处理。8.5
异常的使用5.throwthrow后面带一个类型的实例,throw和catch的关系就像是函数调用,catch指定形参,throw给出实参。throw不一定非要出现在try之后的语句块中,它可以出现在任何需要的地方,只要最终有catch可以捕捉它即可,即使在catch子句中,仍然可以继续throw,这时有两种情况:重抛异常,重抛异常时throw不带参数。throw一个新类型的异常。8.5
异常的使用try{ …}catch(int){
//抛出一个MyException类型的异常
throwMyException("myexception"); }catch(float){
throw; //重新抛出当前的浮点数类型异常}8.5
异常的使用在函数声明后的throw关键字称为异常规范,通过异常规范,在函数声明中,可以指定该函数能够直接或间接抛出的异常集合(可不抛出任何异常),异常规范保证函数不会抛出未列出的异常,例如:voidfoo()throw(int); //只能抛出int型异常voidbar()throw();
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 私人房产附属设施买卖合同
- 清关代理合同协议书
- 基于情境学习的数学逻辑思维培养教学方案
- 智能化产业园区管理平台合作协议
- 智能家居产品研发及销售协议
- 电子商务退换货免责条款
- 超市食材进销存协议
- 混凝土水泥买卖合同
- 自来水管理承包合同
- 血液 课件-2024-2025学年北师大版生物七年级下册
- 标准土方工程招标文件样本
- 如何提升管理能力和水平
- 智慧渔政网格管理平台项目方案
- GB/T 7716-2024聚合级丙烯
- 《弱电知识培训》课件
- 丹麦地理课件
- 住宅小区供配电设施建设和改造技术标准
- 劳动合同(模版)4篇
- 100道公安基础知识题目训练含答案
- 口腔耗材采购合同范本
- JBT 14682-2024 多关节机器人用伺服电动机技术规范(正式版)
评论
0/150
提交评论