当前课程知识点:基于Linux的C++ >  第十一讲 泛型编程 >  11.5 运行期型式信息(一) >  LinuxCPP1105

返回《基于Linux的C++》慕课在线视频课程列表

LinuxCPP1105在线视频

LinuxCPP1105

下一节:LinuxCPP1106

返回《基于Linux的C++》慕课在线视频列表

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 子句里

这样同样是合法且有效的

还记得我以前留过的一道思考题吗

那个思考题是让同学们处理

一个数据的持久化的

我当时说了 一个数据从文件中读进来

还是从其他的地方读进来

对这个数据是不是需要持久化是有影响的

在这种情况下 没有异常处理机制

处理的模式就不方便

它的那个实现策略

就会和这个非常得相似

基于Linux的C++课程列表:

第一讲 C/C++基本语法元素

-1.1 提纲

--LinuxCPP0101

-1.2 程序设计的基本概念

--LinuxCPP0102

-1.3 简单C/C++程序介绍

--LinuxCPP0103

-1.4 程序设计的基本流程

--LinuxCPP0104

-1.5 基本语法元素

--LinuxCPP0105

-1.6 程序设计风格

--LinuxCPP0106

-1.7 编程实践

--LinuxCPP0107

-第一讲 C/C++基本语法元素--编程实践提交入口

第二讲 程序控制结构

-2.1 提纲

--LinuxCPP0201

-2.2 结构化程序设计基础

--LinuxCPP0202

-2.3 布尔数据

--LinuxCPP0203

-2.4 分支结构

--LinuxCPP0204

-2.5 break语句

--LinuxCPP0205

-2.6 循环结构

--LinuxCPP0206

-2.7 编程实践

--LinuxCPP0207

-第二讲 程序控制结构--编程实践提交入口

第三讲 函数

-3.1 提纲

--LinuxCPP0301

-3.2 函数声明、调用与定义

--LinuxCPP0302

-3.3 函数调用栈框架

--LinuxCPP0303

-3.4 编程实践

--LinuxCPP0304

-第三讲 函数--编程实践提交入口

第四讲 算法

-4.1 提纲

--LinuxCPP0401

-4.2 算法概念与特征

--LinuxCPP0402

-4.3 算法描述

--LinuxCPP0403

-4.4 算法设计与实现

--LinuxCPP0404

-4.5 递归算法(一)

--LinuxCPP0405

-4.6 递归算法(二)

--LinuxCPP0406

-4.7 容错与计算复杂度

--LinuxCPP0407

-4.8 编程实践

--LinuxCPP0408

-第四讲 算法--编程实践提交入口

第五讲 程序组织与开发方法

-5.1 提纲

--LinuxCPP0501

-5.2 库与接口

--LinuxCPP0502

-5.3 随机数库(一)

--LinuxCPP0503

-5.4 随机数库(二)

--LinuxCPP0504

-5.5 作用域与生存期

--LinuxCPP0505

-5.6 典型软件开发流程(一)

--LinuxCPP0506

-5.7 典型软件开发流程(二)

--LinuxCPP0507

-5.8 编程实践

--LinuxCPP0508

-第五讲 程序组织与开发方法--编程实践提交入口

第六讲 复合数据类型

-6.1 提纲

--LinuxCPP0601

-6.2 字符

--LinuxCPP0602

-6.3 数组(一)

--LinuxCPP0603

-6.4 数组(二)

--LinuxCPP0604

-6.5 结构体

--LinuxCPP0605

-6.6 编程实践

--LinuxCPP0606

-第六讲 复合数据类型--编程实践提交入口

第七讲 指针与引用

-7.1 提纲

--LinuxCPP0701

-7.2 指针基本概念

--LinuxCPP0702

-7.3 指针与函数

--LinuxCPP0703

-7.4 指针与复合数据类型(一)

--LinuxCPP0704

-7.5 指针与复合数据类型(二)

--LinuxCPP0705

-7.6 字符串

--LinuxCPP0706

-7.7 动态存储管理(一)

--LinuxCPP0707

-7.8 动态存储管理(二)

--LinuxCPP0708

-7.9 引用

--LinuxCPP0709

-7.10 编程实践

--LinuxCPP0710

-第七讲 指针与引用--编程实践提交入口

第八讲 链表与程序抽象

-8.1 提纲

--LinuxCPP0801

-8.2 数据抽象(一)

--LinuxCPP0802

-8.3 数据抽象(二)

--LinuxCPP0803

-8.4 链表(一)

--LinuxCPP0804

-8.5 链表(二)

--LinuxCPP0805

-8.6 链表(三)

--LinuxCPP0806

-8.7 链表(四)

--LinuxCPP0807

-8.8 函数指针(一)

--LinuxCPP0808

-8.9 函数指针(二)

--LinuxCPP0809

-8.10 抽象链表(一)

--LinuxCPP0810

-8.11 抽象链表(二)

--LinuxCPP0811

-8.12 编程实践

--LinuxCPP0812

-第八讲 链表与程序抽象--编程实践提交入口

第九讲 类与对象

-9.1 提纲

--LinuxCPP0901

-9.2 程序抽象与面向对象

--LinuxCPP0902

-9.3 类类型

--LinuxCPP0903

-9.4 对象(一)

--LinuxCPP0904

-9.5 对象(二)

--LinuxCPP0905

-9.6 类与对象的成员(一)

--LinuxCPP0906

-9.7 类与对象的成员(二)

--LinuxCPP0907

-9.8 类与对象的成员(三)

--LinuxCPP0908

-9.9 继承(一)

--LinuxCPP0909

-9.10 继承(二)

--LinuxCPP0910

-9.11 继承(三)

--LinuxCPP0911

-9.12 多态(一)

--LinuxCPP0912

-9.13 多态(二)

--LinuxCPP0913

-9.14 编程实践

--LinuxCPP0914

-第九讲 类与对象--编程实践提交入口

第十讲 操作符重载

-10.1 提纲

--LinuxCPP1001

-10.2 四则运算符重载(一)

--LinuxCPP1002

-10.3 四则运算符重载(二)

--LinuxCPP1003

-10.4 关系与下标操作符重载

--LinuxCPP1004

-10.5 赋值操作符重载(一)

--LinuxCPP1005

-10.6 赋值操作符重载(二)

--LinuxCPP1006

-10.7 赋值操作符重载(三)

--LinuxCPP1007

-10.8 赋值操作符重载(四)

--LinuxCPP1008

-10.9 赋值操作符重载(五)

--LinuxCPP1009

-10.10 流操作符重载(一)

--LinuxCPP1010

-10.11 流操作符重载(二)

--LinuxCPP1011

-10.12 流操作符重载(三)

--LinuxCPP1012

-10.13 操作符重载总结

--LinuxCPP1013

-10.14 编程实践

--LinuxCPP1014

-第十讲 操作符重载--编程实践提交入口

第十一讲 泛型编程

-11.1 提纲

--LinuxCPP1101

-11.2 泛型编程概览

--LinuxCPP1102

-11.3 异常处理机制(一)

--LinuxCPP1103

-11.4 异常处理机制(二)

--LinuxCPP1104

-11.5 运行期型式信息(一)

--LinuxCPP1105

-11.6 运行期型式信息(二)

--LinuxCPP1106

-11.7 模板与型式参数化

--LinuxCPP1107

-11.8 题外话:术语翻译

--LinuxCPP1108

-11.9 泛型编程实践(一)

--LinuxCPP1109

-11.10 泛型编程实践(二)

--LinuxCPP1110

-11.11 泛型编程实践(三)

--LinuxCPP1111

-11.12 泛型编程实践(四)

--LinuxCPP1112

-11.13 泛型编程实践(五)

--LinuxCPP1113

-11.14 泛型编程实践(六)

--LinuxCPP1114

-11.15 泛型编程实践(七)

--LinuxCPP1115

-11.16 泛型编程实践(八)

--LinuxCPP1116

-11.17 泛型编程实践(九)

--LinuxCPP1117

-11.18 泛型编程实践(十)

--LinuxCPP1118

-11.19 编程实践

--LinuxCPP1119

-第十一讲 泛型编程--编程实践提交入口

第十二讲 Linux系统编程基础

-12.1 提纲

--LinuxCPP1201

-12.2 程序执行环境(一)

--LinuxCPP1202

-12.3 程序执行环境(二)

--LinuxCPP1203

-12.4 程序执行环境(三)

--LinuxCPP1204

-12.5 程序执行环境(四)

--LinuxCPP1205

-12.6 输入输出(一)

--LinuxCPP1206

-12.7 输入输出(二)

--LinuxCPP1207

-12.8 文件系统

--LinuxCPP1208

-12.9 设备

--LinuxCPP1209

-12.10 库(一)

--LinuxCPP1210

-12.11 库(二)

--LinuxCPP1211

-12.12 makefile文件(一)

--LinuxCPP1212

-12.13 makefile文件(二)

--LinuxCPP1213

-12.14 makefile文件(三)

--LinuxCPP1214

-12.15 编程实践

--LinuxCPP1215

-第十二讲 Linux系统编程基础--编程实践提交入口

第十三讲 进程编程

-13.01 提纲

--LinuxCPP1301

-13.02 进程基本概念

--LinuxCPP1302

-13.03 信号

--LinuxCPP1303

-13.04 进程管理(一)

--LinuxCPP1304

-13.05 进程管理(二)

--LinuxCPP1305

-13.06 进程管理(三)

--LinuxCPP1306

-13.07 进程间通信(一)

--LinuxCPP1307

-13.08 进程间通信(二)

--LinuxCPP1308

-13.09 进程间通信(三)

--LinuxCPP1309

-13.10 进程间通信(四)

--LinuxCPP1310

-13.11 进程池

--LinuxCPP1311

-13.12 编程实践

--LinuxCPP1312

-第十三讲 进程编程--编程实践提交入口

第十四讲 线程编程

-14.1 提纲

--LinuxCPP1401

-14.2 线程基本概念

--LinuxCPP1402

-14.3 线程管理(一)

--LinuxCPP1403

-14.4 线程管理(二)

--LinuxCPP1404

-14.5 线程管理(三)

--LinuxCPP1405

-14.6 线程管理(四)

--LinuxCPP1406

-14.7 线程同步机制(一)

--LinuxCPP1407

-14.8 线程同步机制(二)

--LinuxCPP1408

-14.9 C++11线程库(一)

--LinuxCPP1409

-14.10 C++11线程库(二)

--LinuxCPP1410

-14.11 C++11线程库(三)

--LinuxCPP1411

-14.12 C++11线程库(四)

--LinuxCPP1412

-14.13 C++11线程库(五)

--LinuxCPP1413

-14.14 编程实践

--LinuxCPP1414

-第十四讲 线程编程--编程实践提交入口

第十五讲 网络编程

-15.1 提纲

--LinuxCPP1501

-15.2 Internet网络协议

--LinuxCPP1502

-15.3 套接字(一)

--LinuxCPP1503

-15.4 套接字(二)

--LinuxCPP1504

-15.5 编程实践

--LinuxCPP1505

-第十五讲 网络编程--编程实践提交入口

课程文档

-课程PDF文件

LinuxCPP1105笔记与讨论

也许你还感兴趣的课程:

© 柠檬大学-慕课导航 课程版权归原始院校所有,
本网站仅通过互联网进行慕课课程索引,不提供在线课程学习和视频,请同学们点击报名到课程提供网站进行学习。