C++-运行时类型信息,异常(day11)
一、运行时类型信息
1、typeid运算符
头文件:#include<typeinfo>
C++的标准头文件,都对应相应的类
//sizeof(类型/变量/表达式),返回内存大小
typeid(类型/变量/表达式),返回typeinfo类型的对象,其中包含name()成员函数,返回字符串,描述类型信息
虽然与函数调用形式相同,但是typeid是操作符。
int x;cout<<typeid(int).name()<<endl;//不同编译器的字符串描述可能不同cout<<typeid(x).name()<<endl; cout<<typeid(int* [5]).name()<<endl; cout<<typeid(int(*) [5]).name()<<endl;//数组指针
class X{protected:virtual void foo(void){} };class Y{protected:void foo(void){} }class Z{void foo(void){}};void func(X& x){/*if(!strcmp(typeid(x).name(),"1Y")){cout<<"Y"<<endl;}else if(!strcmp(typeid(x).name(),"1Z")){cout<<"Z"<<endl;}else(!strcmp(typeid(x).name(),"1X")){cout<<"X"<<endl;}*/ //typeinfo类中已经重载的operator==函数if(!strcmp(typeid(x).==typeid(Y))){cout<<"Y"<<endl;}else if(!strcmp(typeid(x)==typeid(Z))){cout<<"Z"<<endl;}else(!strcmp(typeid(x)==typeid(X)){cout<<"X"<<endl;}}
注意:
typeid的使用是基于基类中存在虚函数,并且子类继承并覆盖了虚函数。否则typeid无法获取类型信息。
2、动态类型转换运算符(day3)
目标类型变量=dynamic_cast<目标类型>目标源类型变量
使用场景:适用于具有多态继承关系的父子类指针或者引用之间的显式转换。
class A{virtual void foo(void){}};class B:publicA{void foo(void)};class C:publicA{void foo(void)};class D{};B b;A* pa=&b;//B* pb=pa;//向下转换,编译报错//B* pb=static_cast<B*>(pa);//ok B* pb=dynamic_cast<B*>(pa);//ok//C* pc=static_cast<C*>(pa);//因为pa已经指向了B*的类型,虽然可以通过,但是不安全 C* pc=dynamic_cast<C*>(pa);//程序执行阶段进行转换,而静态转换是程序编译阶段进行转换,虽然不报错,但是pc的地址将为空。执行时动态类型转换会做类型检查,如果是无关的类,无法转换。 D* pc=dynamic_cast<D*>(pa);//也为空//可以打印出pa,pb,pc的地址,可见pc为空地址//使用引用的不合理类型动态转换时,执行时会进程会被终止
dynamic_cast在转换的过程中,会根据多态的特性,检查父子类的指针或者引用目标类型是否一致,如果一致则转换成功,否则转换失败,如果是指针转换,则返回NULL,如果是引用转换,则抛出异常“bad_cast”
二、异常(Exception)
1、常见错误
1)语法错误
2)逻辑错误
3)功能错误
4)设计缺陷
5)需求不符
6)环境异常
7)操作不当
2、C的错误处理机制
1)通过返回值表示错误
class A{public:A(void){cout<<"A::A()"<<endl;}~A(void){cout<<"~A::A()"<<endl;}};//通过返回值表示错误int func3(void){A a;FILE* fp=fopen("none.text","r");if(fp==NULL){cout<<"file open error!"<<endl;return -1;}fclose(fp)return 0;}int func2(void){A a;if(func3()==-1){return -1;}return 0;}int func1(void){A a;if(func2()==-1){return -1;}//... retuern 0;}int main(void){if(func1()==-1){return -1;}//...return 0;}//通过返回值表示错误
栈区对象在出现异常之后,能够正常释放
2)通过远程跳转来处理错误
jmp_buf g_env;//包含头文件#include<setjmp.h>
class A{public:A(void){cout<<"A::A()"<<endl;}~A(void){cout<<"~A::A()"<<endl;}};//通过返回值表示错误int func3(void){A a;FILE* fp=fopen("none.text","r");if(fp==NULL){longjmp(g_env,-1);}//...fclose(fp)return 0;}int func2(void){A a;func3();return 0;}int func1(void){A a;func2();//...retuern 0;}int main(void){if(setjmp(g_nev)==-1){//先设置g_env,如果有错误,会再次直接跳转到此处 cout<<"file open error!"<<endl; } func1();//...return 0;}
使用远程跳转栈区对象无法得到释放
通过返回值表示错误
优点:函数调用路径中所有的局部对象都能够得到正常的析构,不会内存泄漏。
缺点:错误处理流程比较复杂,逐层判断,代码臃肿
通过盐城跳转机制处理错误
优点:不需要逐层判断,实现一步到位的错误处理,代码精简
缺点:函数调用的路径中布局对象失去被析构的机会,形成内存析构
3、C++的异常处理机制
结合C中两种错误处理的优点,同时避免他们的缺点,在形式上实现一步到位的错误处理,无需逐层判断返回值,所有的局部对象得到正常的析构。
class A{public:A(void){cout<<"A::A()"<<endl;}~A(void){cout<<"~A::A()"<<endl;}};//通过返回值表示错误int func3(void){A a;FILE* fp=fopen("none.text","r");if(fp==NULL){throw -1;//抛出异常 }//... fclose(fp)return 0;}int func2(void){A a;func3();return 0;}int func1(void){A a;func2();//... retuern 0;}int main(void){try{func1();//...出现异常,此处将不会得到执行,直接跳转到catch }catch(int ex/*抛出的异常数据类型*/){cout<<"file open error!"<<endl;return -1;}//...return 0;}
如果执行到throw语句,会逐层返回执行},并且内存会得到释放
4、C++异常语法
(1)异常抛出
throw 异常对象;//抛出的异常会被放到安全区,无法手动访问
如:
throw -1;
throw "file error";
throw 对象;
(2)异常捕获
try{
//可能引发异常的语句
}
catch(异常类型1){
//异常类型1的处理
}
catch(异常类型2){
//异常类型2的处理
}
...
catch(.../*可以匹配任意类型*/){
//针对其他类型的处理
}
class FileError{ public:FileError(){}FileError(const string& file,int line):m_file(file),m_line(line){cout<<"抛出位置"<<m_file<<","<<m_line;} private:string m_file;int m_line; }; class A{public:A(void){cout<<"A::A()"<<endl;}~A(void){cout<<"~A::A()"<<endl;}};//通过返回值表示错误int func3(void){A a;FILE* fp=fopen("none.text","r");if(fp==NULL){throw FileError(__FILE__,__LINE__);/*注意,是双下划线,而不是单下划线__FILE__ 包含当前程序文件名的字符串__LINE__ 表示当前行号的整数__DATE__ 包含当前日期的字符串__STDC__ 如果编译器遵循ANSI C标准,它就是个非零值__TIME__ 包含当前时间的字符串*///FileError ex;//throw ex;throw "file error";throw -1;//抛出异常 }//... fclose(fp)return 0;}int func2(void){A a;func3();return 0;}int func1(void){A a;func2();//... retuern 0;}int main(void){try{func1();//...出现异常,此处将不会得到执行,直接跳转到catch }catch(int ex/*抛出的异常数据类型*/){cout<<"file open error!"<<endl;return -1;}catch(const char* ex){cout<<"file error!"<<endl;return -1;}catch(FileError& ex){//如果抛出的是对象,最好是用引用cout<<"FileError"<<endl;return -1;}//...return 0;}
注意:
(1)如果没有类型可以匹配,那么抛出的异常将被系统锁捕获,进程将被回收。内存被释放
(2)如果有两个连续的throw语句,只会被执行一个,因为在throw时候,直接跳转到}执行。
(3)如果抛出的是类的对象,最好使用引用。
(4)在面向对象的编程中,一般都是抛出对象,而不是整数或者字符串等基本类型,因为类比基本类型可以存储更多信息。比如日志等
5、异常-扩展
class A{};class B:public A{};void func(void){//...throw(B);//throw(A); }int main(void){try{func();}catch(A& ex){//向上造型可以匹配B类异常 cout<<"捕获到A类异常"<<endl;return -1;}catch(B& ex){cout<<"捕获到B类的异常"<<endl;return -1;}return 0;}//上述代码中B异常将无法被捕获,无论是抛A,还是抛B,正确的处理方式应该把子类的异常捕获放在基类之前,防止发生向上造型。
注意:
catch的匹配是自上而下进行匹配,而不是选择最优匹配,所以应该把子类的异常捕获放在基类之前,防止发生向上造型。
6、异常说明1)可以在函数原型中增加异常说明,说明该函数可能抛出的异常类型。提前通知编译器,函数会抛出的异常类型
返回类型 函数名(形参表)[cosnt]throw(异常类型表){...}
不加异常说明列表,异常也能够被正常捕获,和不加的区别在于,如果函数抛出了与说明列表不符的类型,这个异常将不会被捕获。自然也会被系统所捕获。 2)函数的异常说明只是一种承诺,表示该函数不会抛出说明列表意外的类型。意外的异常将会被系统所捕获。 3)如果不写异常说明,表示可以抛出任何异常 4)空异常说明,throw(),表示不会抛出任何异常。 5)如果函数的声明和定义分开,在声明和定义部分都要加上异常说明。并且说明列表必须相同,但是顺序可以改变。 7、异常说明与多态
class FileError{}; class MemError{};class Base{ public:virtual void func(void)throw(FileError,MemError){} };class Derived:public Base{ public:void func(void){}//虚函数覆盖会失败,因为子类的虚函数覆盖函数没有异常说明,这里异常说明范围可以缩小,但是不能扩大 };
如果基类中的虚函数带有异常说明,它的子类中,该函数的覆盖版本不能比基类版本抛出更多异常,否则编译器报出“放松throw限定”错误
转载于:https://www.cnblogs.com/ptfe/p/11300823.html
C++-运行时类型信息,异常(day11)相关推荐
- C++ - RTTI(RunTime Type Information)运行时类型信息 详解
RTTI, RunTime Type Information, 运行时类型信息, 是多态的主要组成部分, 通过运行时(runtime)确定使用的类型, 执行不同的函数,复用(reuse)接口. dy ...
- wxWidgets:运行时类型信息 (RTTI)
wxWidgets:运行时类型信息 (RTTI) wxWidgets:运行时类型信息 (RTTI) 类信息 RTTI例子 wxWidgets:运行时类型信息 (RTTI) C++ 的一个缺点曾经是没有 ...
- [面试] C/C++ 语法(六)—— RTTI(运行时类型信息)
RTTI(RunTime Type Information),顾名思义,对象运行时类型信息,以便在运行时进行类型识别. C++ 的对象识别可通过以下三个技术得以实现: (1)dynamic_cast ...
- C++运行时类型信息 (RTTI)
dynamic_cast 用于多态类型的转换 typeid typeid 运算符允许在运行时确定对象的类型 type_id 返回一个 type_info 对象的引用 如果想通过基类的指针获得派生类的数 ...
- C++知识点54——RTTI(运行时类型识别)
一.RTTI概述 RTTI的功能由两个运算符实现,一个是typeid,用来返回表达式的类型:另一个是dynamic_cast,作用是将基类的指针或引用安全地转为子类的指针或引用 二.typeid ty ...
- java 运行时类型_Java基础之RTTI 运行时类型识别
运行时类型识别(RTTI, Run-Time Type Identification)是Java中非常有用的机制,在Java运行时,RTTI维护类的相关信息. 多态(polymorphism)是基于R ...
- Java RTTI运行时类型识别
RTTI(Run-Time Type Identification),通过运行时类型信息程序能够使用基类的指针或引用来检查这些指针或引用所指的对象的实际派生类型. RTTI提供了以下两个非常有用的操作 ...
- java rtti_举例讲解Java的RTTI运行时类型识别机制
1.RTTI:运行时类型信息可以让你在程序运行时发现和使用类型信息. 在Java中运行时识别对象和类的信息有两种方式:传统的RTTI,以及反射.下面就来说下RTTI. RTTI:在运行时,识别一个对象 ...
- c++ RTTI(运行时类型识别)
通过RTTI,能够通过基类的指针或引用来检索其所指对象的实际类型.c++通过下面两个操作符提供RTTI. (1)typeid:返回指针或引用所指对象的实际类型. (2)dynamic_cast:将基类 ...
最新文章
- java 关键字(面试题)
- c库的rand/random随机数产生函数性能差?
- python数据挖掘主要特点_python数据挖掘(从数据集中抽取特征)
- SmartNIC/DPU — TSO、GSO、LRO、GRO 卸载技术
- 求首尾相接的数组的最大子数组和
- Bitcoin 中的挖矿算法(2) 难度值说明
- LeetCode Algorithm 389. 找不同
- 获取Windows mobile 开始菜单里最近打开的程序(Recent Programs)
- sed 和 awk 正则表达式
- MyCat的安装及基本使用(MySQL分库分表)
- 做朋友圈需先从做人开始
- UniWebView 3 使用心得
- 39、VS838红外线接收实验
- Abaqus Ncode振动疲劳分析教程
- 整数规划:分支定界法
- ubuntu 出现WIFi设备未就绪的解决办法
- ie浏览器flash player不能用的解决方案
- 《web前端面试题》第一问-如何快速居中对齐?
- ROS2 第一个C++程序(talker和listener为例)
- rasa开发过程中出现的错误情况(实时更新)