当前课程知识点:基于Linux的C++ > 第十一讲 泛型编程 > 11.5 运行期型式信息(一) > LinuxCPP1105
接下来要补充的第二个知识点
就是运行期型式信息
我们前面不讲过了吗
在面向对象架构里边
我们频繁地需要
将一个对象或者一个对象的引用
或者一个指向对象的指针
沿着类库的继承层次进行转型
这个转型必须能够在程序运行期间
确保它的有效性
而这个有效性的保证
没有运行期的型式信息
它是不可能完成的
运行期的型式信息
就意味着在程序运行期间
我们要保存这些对象的
所从属的型式的信息 这个就是RTTI
在 C++ 标准库里面提供了一个 type_info
这样的一个型式信息类
用来记录这些类的型式信息
当然你并不能够直接使用 type_info 类
你只能使用 typeid 操作符
来获取一个特定的表达式的型式信息
在实现的过程中 进行对象或对象的引用
或指向对象的指针的转型的时候
C++ 为我们提供了新的更安全的关键字
而不是早期的型式转换的模式
这四个关键字中最重要的就是 dynamic_cast
它表示动态转型
另外三个是 static_cast 表示静态转型
reinterpret_cast 表示复诠转型
const_cast 表示常量转型
这四个转型操作符都使用了模板
我们这一章要讨论的模板
我们首先来看 typeid 操作符和 type_info 类
type_info 类
编译器实现的动态型式信息
都在这个类里边
它保存的是什么东西呢
事实上 它保存的是我们的动态型式信息
它本身是一个类型
这是一定要注意的
所以它是动态型式信息型式
主要用于在程序运行时保存数据对象的型式信息
主要用于在程序运行时保存数据对象的型式信息
程序运行期间
那么我一个对象 这样一个对象
它的型式信息都要通过 type_info 来保存
还是我刚才讲的
不能够直接使用 type_info 类
只能通过 typeid 操作符来访问它
有一个成员函数名字叫 name()
你可以用它来获得类的名称
我们看下面这样一个例子
你要包含的就是 typeinfo 这个头
假设我有一个类
名字叫 Programmer
定义了它的一个对象 p
我还有一个雇员类 Employee
又定义了 Employee 这个类的一个引用 e
然后把它初始化成 p
我们可以通过 typeid 操作符在 e 上做运算
实际上将得到 e 所对应的那一个类
可以获取它的名字 name()
我们实际上将获得的是“Programmer”
而不是“Employee”
很清楚吧 当然这里有一个前提假设
Programmer 是 Employee 的一个派生类
我们来看第一个关键字 dynamic_cast
它完成动态转型
动态转型有三种方式
一个是向上转型
就是沿着类的继承层次向基类转型
这叫向上
一个是向下转型
就是沿着类的继承层次向派生类进行转型
还有第三个 就是交叉转型
什么叫交叉转型呢
就是在类库的层次架构里边
你不构建了很多个类吗
它们是多继承的关系
相互之间是非常复杂的关系嘛
那么当你想进行交叉转型的时候
你就可以通过 dynamic_cast
沿着类的多重继承的层次进行横向转型
这个就叫交叉转型
对于一个指针的动态转型
当它正确执行的时候
返回的就是指向目标类对象的一个指针
当它错误执行的时候返回值就是 0 或 NULL
在 C++11 将是 nullptr
nullptr 这样一个关键字 同样是空指针啦
我们前面不谈到过嘛
如果是一个引用
那么在进行动态转型的时候
如果正确执行
返回的当然是对目标类对象的引用
如果是错误执行了
那么它将会引发一个 bad_cast 异常
我们必须对这个异常进行处理
dynamic_cast 本身是一个模板
我们前面谈到过
我们看一个例子 你就明白怎么使用它了
假设软件公司
包括程序员和经理这两类雇员
需要按照不同的规则来支付薪水和奖金
我们怎么来实现这样的功能
来看它的类的架构
首先我们有 Employee 的一个类
在这里我实现两个虚函数 PaySalary()、PayBonus()
一个是付薪水 一个是付奖金
然后有一个 Manager 类
当然它继承自 Employee
这两个虚函数仍然要重新实现
此外 我们还有 Programmer 类
程序员类 它同样继承自 Employee
最后我们有一个 Company
有一个公司的类
我这里实现了两个 PayRoll()
付薪水的这样的函数
这两个函数当然一般情况下
我们只需要实现一个
这里只是为了示例
一个公司包括很多个雇员
肯定不是一个、两个 对吧
好多个雇员
所以我们使用一个向量来保存这些雇员
所谓向量就是我们这一讲
标准模板库里边
会特别讨论的一个数据结构
现在知道这么一回事就行了
我们这里边总共 4 个类
你注意关系 Employee 是基类
派生出了 Manager 和 Programmer
Company 和它们并没有继承关系
现在我们要实现 Company 的 PayRoll() 成员函数
我们要付薪水、付奖金
看上去很清楚
但这个程序代码是有问题的
因为我们传过来的
是一个指向 Employee 的指针
你可以在这个 Employee 上付薪水、付奖金
问题是 刚才不是说了吗
要按照不同的规则
为经理或程序员来付薪水和奖金
两者是不一样的
那么你怎么处理这个问题啊
你比如说 如果是程序员
我就要付薪水 还要付奖金
如果是经理 那我就只付薪水就够了
他薪水比程序员高嘛 对吧
在这种情况下对经理我只付薪水
对于程序员我才又付薪水又付奖金
它有一个绩效 这样的一个逻辑
在第一个版本里面是没有办法实现的
因为我们传过来的是 Employee 的指针
程序运行期间
我的程序代码本身要能够判定
e 所指向的那个目标数据对象
它到底是 Programmer 还是 Manager
那么 如果真没有动态转型
这个事 你就做不了
第一版是有问题的
那么第二版我们就要使用动态转型
定义一个指向 Programmer 的指针 p
把它初始化 dynamic_cast〈Programmer *〉(e)
什么意思 就是把 e 这个指向 Employee 的指针
动态转型成指向 Programmer 的一个指针
当它转型成功的时候
p 将指向那个目标类的对象
如果转型不成功
p 值将是NULL 转型完了之后
if( p ) 看 p 值是不是 NULL
p 值不是 NULL
说明它确确实实是程序员
e 这个指针指向的是一个程序员对象
好的 我们就在 p 指针上调用
p->PaySalary(),p->PaySalary()
付薪水、付奖金
否则在 e 上边付薪水 逻辑很清晰吧
你一看 dynamic_cast
它的作用就体现出来了
我们前面讲了 dynamic_cast 这个关键字
它是一个模板
后面要用“〈〉”带着一个目标型的一个描述
目标型必须写在那个“〈〉”里
这是模板的基本架构
必须按照这个方式来
小括号对后面才是要待转型的那个对象
按照这个方式才能工作
一定要会使用它
第三版 如果它是一个引用呢
我们刚才那个第二版
传的是指向 Employee 的一个指针
那如果它是一个 Employee 的引用呢
我们怎么 PayRoll()
我们实现的这是版本三
因为对于一个引用来讲
如果动态转型失败
它没有一个空引用给你返回
它返回的是一个 bad_cast 的异常
这个异常是在标准库里边
已经替我们定义好的
所以为了处理这个异常
我们必须使用 try ... catch 这个异常处理机制
我们程序代码应该是这个样子
try Programmer & p
定义 Programmer 的一个引用
然后把它初始化成 dynamic_cast〈Programmer &〉(e)
转型成功 p.PaySalary(); p.PayBonus();
付薪水、付奖金
转型不成功 它就会引发 bad_cast 异常
所以我们的 catch 子句将捕获这个异常
然后我们干什么事情 付薪水
他不是程序员 我就付薪水
说明他是什么呢 经理
我们就两种成员
要么是程序员 要么是经理
他不是程序员 我就付薪水
这个代码有点特殊
我们在 catch 子句里处理的其实不是错误
而是一个非正常情况
但你不能说公司里面只有程序员
是经理就不正常
这个... 不是这个意思
这个意思就是说 我这个程序代码中
优先处理的对象是程序员
这是我们程序流程处理过程中
比较正常的情况
当他不是程序员的时候
就出现了一个不那么正常的情况
那么我就把这段代码放到了 catch 子句里
它并不是错误处理
而是为了完成特定的程序功能而编写的代码
放在我们 catch 子句里
这样同样是合法且有效的
还记得我以前留过的一道思考题吗
那个思考题是让同学们处理
一个数据的持久化的
我当时说了 一个数据从文件中读进来
还是从其他的地方读进来
对这个数据是不是需要持久化是有影响的
在这种情况下 没有异常处理机制
处理的模式就不方便
它的那个实现策略
就会和这个非常得相似
-1.1 提纲
-1.2 程序设计的基本概念
-1.3 简单C/C++程序介绍
-1.4 程序设计的基本流程
-1.5 基本语法元素
-1.6 程序设计风格
-1.7 编程实践
-第一讲 C/C++基本语法元素--编程实践提交入口
-2.1 提纲
-2.2 结构化程序设计基础
-2.3 布尔数据
-2.4 分支结构
-2.5 break语句
-2.6 循环结构
-2.7 编程实践
-第二讲 程序控制结构--编程实践提交入口
-3.1 提纲
-3.2 函数声明、调用与定义
-3.3 函数调用栈框架
-3.4 编程实践
-第三讲 函数--编程实践提交入口
-4.1 提纲
-4.2 算法概念与特征
-4.3 算法描述
-4.4 算法设计与实现
-4.5 递归算法(一)
-4.6 递归算法(二)
-4.7 容错与计算复杂度
-4.8 编程实践
-第四讲 算法--编程实践提交入口
-5.1 提纲
-5.2 库与接口
-5.3 随机数库(一)
-5.4 随机数库(二)
-5.5 作用域与生存期
-5.6 典型软件开发流程(一)
-5.7 典型软件开发流程(二)
-5.8 编程实践
-第五讲 程序组织与开发方法--编程实践提交入口
-6.1 提纲
-6.2 字符
-6.3 数组(一)
-6.4 数组(二)
-6.5 结构体
-6.6 编程实践
-第六讲 复合数据类型--编程实践提交入口
-7.1 提纲
-7.2 指针基本概念
-7.3 指针与函数
-7.4 指针与复合数据类型(一)
-7.5 指针与复合数据类型(二)
-7.6 字符串
-7.7 动态存储管理(一)
-7.8 动态存储管理(二)
-7.9 引用
-7.10 编程实践
-第七讲 指针与引用--编程实践提交入口
-8.1 提纲
-8.2 数据抽象(一)
-8.3 数据抽象(二)
-8.4 链表(一)
-8.5 链表(二)
-8.6 链表(三)
-8.7 链表(四)
-8.8 函数指针(一)
-8.9 函数指针(二)
-8.10 抽象链表(一)
-8.11 抽象链表(二)
-8.12 编程实践
-第八讲 链表与程序抽象--编程实践提交入口
-9.1 提纲
-9.2 程序抽象与面向对象
-9.3 类类型
-9.4 对象(一)
-9.5 对象(二)
-9.6 类与对象的成员(一)
-9.7 类与对象的成员(二)
-9.8 类与对象的成员(三)
-9.9 继承(一)
-9.10 继承(二)
-9.11 继承(三)
-9.12 多态(一)
-9.13 多态(二)
-9.14 编程实践
-第九讲 类与对象--编程实践提交入口
-10.1 提纲
-10.2 四则运算符重载(一)
-10.3 四则运算符重载(二)
-10.4 关系与下标操作符重载
-10.5 赋值操作符重载(一)
-10.6 赋值操作符重载(二)
-10.7 赋值操作符重载(三)
-10.8 赋值操作符重载(四)
-10.9 赋值操作符重载(五)
-10.10 流操作符重载(一)
-10.11 流操作符重载(二)
-10.12 流操作符重载(三)
-10.13 操作符重载总结
-10.14 编程实践
-第十讲 操作符重载--编程实践提交入口
-11.1 提纲
-11.2 泛型编程概览
-11.3 异常处理机制(一)
-11.4 异常处理机制(二)
-11.5 运行期型式信息(一)
-11.6 运行期型式信息(二)
-11.7 模板与型式参数化
-11.8 题外话:术语翻译
-11.9 泛型编程实践(一)
-11.10 泛型编程实践(二)
-11.11 泛型编程实践(三)
-11.12 泛型编程实践(四)
-11.13 泛型编程实践(五)
-11.14 泛型编程实践(六)
-11.15 泛型编程实践(七)
-11.16 泛型编程实践(八)
-11.17 泛型编程实践(九)
-11.18 泛型编程实践(十)
-11.19 编程实践
-第十一讲 泛型编程--编程实践提交入口
-12.1 提纲
-12.2 程序执行环境(一)
-12.3 程序执行环境(二)
-12.4 程序执行环境(三)
-12.5 程序执行环境(四)
-12.6 输入输出(一)
-12.7 输入输出(二)
-12.8 文件系统
-12.9 设备
-12.10 库(一)
-12.11 库(二)
-12.12 makefile文件(一)
-12.13 makefile文件(二)
-12.14 makefile文件(三)
-12.15 编程实践
-第十二讲 Linux系统编程基础--编程实践提交入口
-13.01 提纲
-13.02 进程基本概念
-13.03 信号
-13.04 进程管理(一)
-13.05 进程管理(二)
-13.06 进程管理(三)
-13.07 进程间通信(一)
-13.08 进程间通信(二)
-13.09 进程间通信(三)
-13.10 进程间通信(四)
-13.11 进程池
-13.12 编程实践
-第十三讲 进程编程--编程实践提交入口
-14.1 提纲
-14.2 线程基本概念
-14.3 线程管理(一)
-14.4 线程管理(二)
-14.5 线程管理(三)
-14.6 线程管理(四)
-14.7 线程同步机制(一)
-14.8 线程同步机制(二)
-14.9 C++11线程库(一)
-14.10 C++11线程库(二)
-14.11 C++11线程库(三)
-14.12 C++11线程库(四)
-14.13 C++11线程库(五)
-14.14 编程实践
-第十四讲 线程编程--编程实践提交入口
-15.1 提纲
-15.2 Internet网络协议
-15.3 套接字(一)
-15.4 套接字(二)
-15.5 编程实践
-第十五讲 网络编程--编程实践提交入口