当前课程知识点:基于Linux的C++ >  第八讲 链表与程序抽象 >  8.3 数据抽象(二) >  LinuxCPP0803

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

LinuxCPP0803在线视频

LinuxCPP0803

下一节:LinuxCPP0804

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

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

接下来我们看

最重要的一个知识点——数据封装

数据封装 它的最主要的目的

是将数据的结构细节隐藏起来

我们假设有这样一个结构体

struct DYNINTS

这是一个动态数组的定义

在这里面它有几个字段呢

有4个字段

一个是capacity,一个是count,

一个是items,一个是modified

有这样4个字段

这4个字段是分别用来表达什么的呢

如果你定义了动态数组类型的一个变量

那么capacity将会用来表达

像这样的一个动态数组

它最多可以包含多少个元素

而count就是表示在程序运的某一个时刻

它的元素个数到底是多少个

所以count肯定是小于等于capacity的

items 它是指向整数的一个指针

它就将用来表达那个数组真实元素序列

理论上来讲

你应该使用动态内存分配的方式

来维护真正的整数数组

modified 它是个布尔量

用来表达这个数组有没有发生改变

如果发生改变的话

modified这个量就会变成true

否则就会一直维持着false

简单的4个字段

就构造了动态数组的结构体

那么我们怎么知道动态数组结构体

在程序运行过程中它的某一个变量

当时的元素个数是多少个呢

一个很简单的方案

如果我们有这样一个

动态数组结构体的变量

我们假设它的名字叫a

如果想知道这个结构体里面的元素个数

那么我们就a.count

就能够获得动态数组的元素个数

但是这种方案对于一个复杂的程序来讲

它是不好的

如果你将来改变了结构体的定义

把这个count这个成员的名字改成了

number_of_elements

那么所有使用结构体类型的程序

都需要相应地做出改变

而这个改变是很要命的

因为你可能写了一个函数库给别人用

如果他的程序也跟着你来改

那么实际上这个工作量是巨大的

你对这个库的改变将会必然地传播到

所有库的使用者的程序代码里面去

这实际上是一个非常不好的设计方式

那么我们就想一个办法

就是我们提供一个函数

我为你实现一个函数的接口DiGetCount

“Di”就是DYNINTS这个动态数组的缩写

我们就用这样一个函数

来获取动态数组的元素个数

这是一个很直接的函数

看上去很简单

那个if语句就是用来作为一个测试的

除此之外就是简单的return语句 很典型

但是它给我们提供了一个很好的设计

就是什么呢

因为这样一个数据类型

它上面有数据的表示 还有数据的功能

我们前面不还说了嘛

数据的表示是容易变的

而数据的功能是不容易变的

那么当我设计好这个动态数组库

你使用动态数组的库

你只使用这个动态数组库

所提供的这些操作集的时候

只要DiGetCount这个函数名字不变

函数的参数不变 函数的返回值不变

那么不管这个函数是怎么实现的

不管这个动态数组结构体怎么变

库的使用者的代码实际上不用变

你比如讲这个count我想改成

number_of_elements

然后DiGetCount这个函数接口不变

但是内部这个实现我改成

return a->number_of_elements

这个改变对于库的设计者来讲

他只要重新设计一下库就可以了

而对于库的使用者来讲

他完全不用关心这个问题

因为这个问题对他来说透明的

我们希望通过一个技术手段

能够将整个程序分解成很多个库

并且这些库的修订、升级、改变这样的操作

只要在接口不变的情况下

不影响其它的库

或者我们这些库的使用者

数据封装就是一个最重要的概念

我们要将数据结构的细节隐藏起来

你不把细节藏起来

你就没有办法保证这个库

和其它的库是隔离的

我们怎么实现数据封装呢

就是这里面看到的

要对结构体里面的数据成员

提供相应的存取函数

我们想获取count值 提供DiGetCount

你想设置这个count

那你就提供一个类似的函数叫DiSetCount

这个就叫存取函数,一个是存,一个是取

所以叫Get/Set函数

这是第一个 数据封装

你如果只有数据封装 这个是不够的

我们假设实现那个结构体

你也提供一系列的存取函数

把数据已经封装起来了

这实际上是暗示这个库的使用者

就是如果你想访问结构体里面的成员

你该使用相应的存取函数

而不是直接访问成员的名字

这样的话数据才能封装起来

但只有这些是不够的

因为struct这个结构体类型

它的设计并没有限定用户

不能访问它的数据成员

你提供了DiGetCount这个函数没错

可是你不能限制不使用这个函数

而直接访问那个count成员

也就是说 对于库的使用者来讲

他可以在DiGetCount函数的调用

和直接访问那个count字段之间

自由地切换

你没有办法限定他只能使用存取函数

而不访问这个结构体的数据成员

这是非常重要的一个地方

也就是说 只有数据封装是不够的

数据封装最主要的一个问题就是

库的使用者实际上能够看到这个定义

他不调用 你没有办法限制他

那我怎么办呢

一个简单的解决方案就是

我把这个数据结构的这样数据成员

全部不让你看

你看不见这个数据结构的这些成员

那么这就意味着不能访问它

只能通过存取函数来访问

这是非常重要的一个地方

这个就叫信息隐藏

数据封装和信息隐藏合在一起

才是编写抽象程序的关键

这是最重要的两个核心概念:

数据封装、信息隐藏

那么在C/C++代码里面怎么实现呢

你如果把数据结构写在头文件里

别人就会包含这个库的头文件

一包含这个库的头文件

那么就能够看到这个结构体的定义

看到这个结构体的定义

就没有办法限制他不使用

所以就把结构体的定义从头文件里

给它挪到源文件里

那么对于库的使用者来讲

他事实上只能看到头文件

他看不见源文件

源文件你可以不提供

只提供编译好的二进制代码

他如果只能看到你的头文件

那么事实上就不知道这个库 这个结构体

它到底是如何实现的

就真正地做到了信息隐藏

而我又要保证

他能够正确使用这个结构体

那么就会在头文件里给出

这个结构体的声明

并且提供一个指向这个结构体的指针

我们保证只使用这个指针

来操纵它的目标结构体的数据对象

看这样一个例子

设计能够存储二维平面上点的

抽象数据类型

按照我们数据封装与信息隐藏的基本介绍

那么在库的接口里面

给出结构体POINT的声明

然后给出PPOINT类型

为指向结构体的一个指针

注意 因为我们目标结构体的定义是未知的

那么实际上这样PPOINT形

虽然可以在函数原型里面使用

但是我们实际上不能使用

它的目标结构体的任何内部信息

这些都放在我们这个点库

放在它的头文件里 “point.h”

像这样的一个点库

我们需要提供一系列的操作

因为我们没有结构体类型的真实定义

要保证库的使用者

能够正确地操纵我们这个点库

必须提供一个点库的接口

所以我们这里面提供

PtCreate、PtDestroy这两个函数

分别用来创建一个点的结构体

和销毁点的结构体

因为我们这里面使用到的全都是指针

所以这里面有创建有销毁

他们都将是动态分配的

还有提供一系列的Get/Set函数

比如PtGetValue、PtSetValue

设置或获取这个点的值

然后我们提供两个函数

PtCompare和PtTransformIntoString

一个是比较,一个是转换

PtPrint 最后一个函数

完成点的数据信息的打印

接下来的代码就是点库的实现细节

放在“point.cpp”里

代码本身实际上是非常非常简单的

我们只是将原始的程序换成了一个

符合数据封装与信息隐藏的模式来编写

最主要的 你看PtCreate

我们会构造这样的一个POINT

然后完成一个标准的赋值动作以后(用x和y去构造)

然后我们会返回

然后PtDestroy 我们就销毁点的对象

这些代码你看

实际上都非常非常简单

但是它给我们提供最基本的

抽象数据类型的定义或实现的一个模式

我们都应该按照这样模式来

最主要地 你看我们struct POINT

结构体类型的定义

我们写在“point.cpp”里面

而没有写到“point.h”里面

就是我刚才讲的 我们完成了数据封装

最重要的 接下来你要做的

就是要把这个信息藏起来

不要给别人看 叫信息隐藏

PtPrint按照一个特殊的格式

我们这里面用“(%d,%d)”的格式

完成这个打印 这是PtCompare

这个地方判断的是两者是不是相等

接下来PtTransformIntoString要完成一个

点的数据对象到一个字符串的转化

也是按照“(%d,%d)”的格式进行转化

在这个转化的过程中

它需要使用这个DuplicateString

完成一个字符串复制的动作

因为我们一开始不知道

点转换成一个字符串以后

占多大字节的存储空间

因为数字转化以后

它的字符串长度是可变的

所以我们先使用一个缓冲区把它保存进去

然后我们才把它复制出来

这个就是PtTransformIntoString

它需要使用一个辅助函数DuplicateString

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

LinuxCPP0803笔记与讨论

也许你还感兴趣的课程:

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