当前课程知识点:基于Linux的C++ >  第九讲 类与对象 >  9.13 多态(二) >  LinuxCPP0913

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

LinuxCPP0913在线视频

LinuxCPP0913

下一节:LinuxCPP0914

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

LinuxCPP0913课程教案、知识点、字幕

有一种很特殊的实现

就叫纯虚函数

你就想我们刚才那个例子

三个类:Account、CheckingAccount、SavingsAccount

Account这个账户本身

实际上充当的是一个抽象的账户

也就是说 实际上在它里面

打印一个余额是没有意义的

因为不管怎么样

它要么是结算账户要么是储蓄账户

我们不存在一个不属于储蓄账户

也不属于结算账户的这样一个抽象的账户

在实际的程序运行过程中

这个事情是不存在的

我们也许会有一个这样的指针

也许会有这样一个引用量

但是一定没有真实的Account账户的存在

我们不会构造它的

这是一个非常重要的地方

我们要构造的要么就是结算账户

要么就是储蓄账户

不会构造一个抽象的Account账户

它只是表达我们整个类库设计的时候

最顶层的抽象的一个模式

表示所有的账户类的最顶层的共性

所以为它实现PrintBalance是没有意义的

就像我们的代码中

我实际上是向标准错误流里面

输出一串信息

并不是真正地输出它的账户余额信息

明确了这一点 那我们就说

这样的一个函数实际上我们不该实现它

实现了也没有意义 反正我们也不会调用

那我们怎么办呢

C++就为我们提供了一个

纯虚函数的机制来实现它

像这样的一个纯虚函数

标准的格式就是virtual

后面跟着它的函数原型

然后把它初始化成0

等号还是初始化记号

后面是一个0 其实就是个NULL

就表示这一个函数将没有任何实现

直接就初始化成0

表示这个函数没有代码体的

但不是只声明不定义

只声明不定义 它也叫没有

但是编译器——如果你调用它的时候

就会告诉你它有问题

因为那个函数不存在

所以调用就会出错 编译就通不过

这个呢 一个纯虚的函数

编译器也会告诉你

你不能调用纯虚的函数

但是实际上 这个函数指针是存在的

在这个对象的虚拟表里边

f这个函数的虚拟表指针(虚函数指针)是存在的

只不过那里被填了0

表示这个函数的入口地址是0

函数本身不存在

但是这个指针是存在的

特别注意这一条

所有的带有纯虚函数的类

在我们C++里边都叫纯虚类

更一般地我们其实应该称它为抽象的类

因为我们绝不可能构造这样类的一个对象

语言机制不允许的

因为里面有一个函数没实现

不允许你这么做

这样的话就是一个抽象的一个类

它最主要目的是作为类继承的顶层

一般来讲可能是最上层

也可能如果下一层需要抽象

它还可以是个抽象类

都是按照像这样的格式来定义的

也就是说 它往往都位于类库层次的顶层

它形成的是一个抽象的机制

它不承担实际的工作

这个就叫抽象类

对于类库的程序设计是非常非常有用的

还有一个细节 需要特别强调

就是析构函数 大部分情况下面

我们应该将析构函数定义成虚拟的

也就是虚析构函数

这是在类库继承层次下边

保持整个类的多态性的一个最基本的需要

我们需要用到它 如果是一个非虚的

它实际上就和特定的类是相关的了

当你在指向基类的一个指针上

去销毁它的派生类的那个对象的时候

如果不是一个虚的析构函数

那么实际上是没有办法正确销毁的

所以析构函数往往都是虚的

我们可以看这样的一个例子

定义一个Point的纯虚类

我们前面不讲了吗

我们的那个Point类

它作为一个抽象类的基础

其实是带了x和y

所以它实际上对应的是二维点

而不是真正的抽象的一个点

真正抽象的一个点应该是0维的点

所以属性都不存在

很明确吧 应该是这么写的

那么对于这样一个类来讲

如果我想定义它的一个Print成员函数

显然我不知道该写什么

其实就是我什么都不应该写

好吧 我把Print定义成纯虚函数

virtual void Print() const = 0

这本身是个常函数

因为它不改变数据对象的值

前面是一个virtual 虚函数

要求它保持多态性

因为它没有数据可操作

所以把它定义成纯虚函数 初始化成0

这样的话 Point就表示一个纯虚类

它就是足够抽象的一个顶层逻辑

所有的点都将从我们的class Point派生下去

对于点库 它的一个继承层次来讲

Point就是它们唯一的根

所有的点都从它继承下来

这就是纯虚类在类库设计层次上面

所起到的最重要的一个作用

virtual void Print() const = 0

我们可以在此基础上定义Point2D

虚继承 单根的话 virtual不写 没问题

定义它的构造函数、Get/Set函数

定义它的虚函数Print

我们实现的应该是virtual void Print() const

后面不能再写初始化成0了

再写初始化0 表示它还是纯虚函数

然后Point3D类 Point3D从Point2D继承

添加一个_z成员 然后Print

实现我们的virtual void Print()const这样的一个函数

还要写它的虚函数嘛

3D的数据处理和2D数据肯定也不一样

当然构造函数、Get/Set函数我们也要添上去

接下来就是我们的实现代码

对于2D的Print 我们调用cout输出两维数据

对于3D的这样一个点

我们就用cout输出一个三维的数据x、y、z

写完了 这个就是我们类库的架构的实现

你注意看这三个类 顶层类是Point

然后中间那一层类是Point2D

最底层的那个类是Point3D

这不就形成了类的继承层次了吗

这是基本的类库的架构设计模式

都是按照这个方式写的

在Point类里面 Print是纯虚函数

Point2D和Point3D分别实现了对应的Print函数

接下来是主程序

定义一个pt1 为指向Point类的指针

然后new一个Point2D给它

然后定义一个pt2

同样是一个指向Point的指针

然后new一个Point3D给它

我们会创建一个二维点的对象

也会创建一个三维点的对象

分别把它们的地址传给pt1和pt2

pt1也好 pt2也好 都是指向Point类的指针

都是指向基类的指针

不是指向派生类的指针

但是我们构造的都是派生类的对象

让基类的指针指向派生类

然后我们在基类指针上面调用Print

没有问题

它调用的就都将是派生类的那个虚函数

这是由多态所保证的

要做的就是这个事

它是二维点 我就处理二维点

如果是三维点 我就处理三维点

不管怎么样 它们都是点

这个是非常非常重要的

如果你为它们写了虚析构函数

那实际上很清楚

你在pt1上面删除和pt2上面删除

删除都是它对应的二维点和三维点的那个对象

绝不会删除抽象的那个点的那个对象

特别注意这一条

基于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文件

LinuxCPP0913笔记与讨论

也许你还感兴趣的课程:

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