当前课程知识点:基于Linux的C++ >  第九讲 类与对象 >  9.8 类与对象的成员(三) >  LinuxCPP0908

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

LinuxCPP0908在线视频

LinuxCPP0908

下一节:LinuxCPP0909

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

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

那么还有一版 我们再想一个方案

我们仍然定义一个staticSingleton的指针_s

指向一个单一的静态Singleton对象

然后不实现它的析构函数了

就是说不用管析构函数的

我真释放 怎么办呢

我写一个public的函数

我自己写一个释放函数

因为要操纵的是静态对象

所以我们写静态函数static void Release()

释放那个静态的对象

很有趣的代码:if(_s) { free(_s), _s = NULL; }

销毁_s所指向的目标单子的内存

你注意到了 它奇怪在什么地方

奇怪在这片内存 我们是new出来的

销毁的时候没有delete

我free 我们讲内存管理的时候

我们说什么呀

我们说new和delete要配对

malloc和free要配对

我们要求你必须按照这个方式来

也就是C的内存分配格式是C的内存分配格式

C++的内存分配格式是C++内存分配格式

现在呢 我们new一个数据对象

以C++格式为它分配这个内存 然后free它

以C的格式销毁它的内存

发生了什么事

当我们调用free的时候

它简单地清空那一片存储区

而不调用那个类的析构函数

如果你delete 它要做的事情就是

调用那个目标对象的析构函数

然后清空那段内存

把那段内存还给操作系统

如果你free 它就不调用那个类的析构函数

直接返还那段内存给操作系统

就跳过了这个类的析构函数的调用 ~Singleton

所以不用管 因为我们不调用它

我们直接free它

这样我就能够保证这一段内存区域

能够完整地正确地返还给操作系统

即使它是new出来的

也就是说 在某些特殊的时候

new和free一样是可以配对的

所以如果你要对它做数据持久化操作

简单 在free这个函数前面

完成它的持久化就行了

这就意味着有了Release这个函数

那么你在任何时候想析构这个单子对象

就可以析构这个单子对象 调用它

Singleton::Release 它就会销毁那个单子对象

当你再次调用Singleton::Get的时候

它就会再次创建一个单子对象

这个时候你要注意

销毁的那个单子对象和新创建的那个单子对象

是两个不同的对象

它们存续的时间是不同的

虽然这两个单子对象在程序中

都保证在它们存在的那段时间里

它们都是唯一的

但它们仍然是两个不同的单子对象

当然也有可能放在内存的不同的地方

所以要特别去注意这个问题

这个实现也会有一个小问题 就是什么呢

如果程序员忘了调用Release 怎么办

那就没有被释放嘛

那这个问题的症状和你只调用new没有delete

只malloc没有free有什么差别呢

没有什么差别 性质不是一模一样的吗

所以它也仍然有一些令人不满意的地方

所以如果你要保证在程序结束的时候

要销毁整个程序中出现的所有单子对象

(可能每一个类一个单子对象好多个单子类呢)

那么你必须在程序结束之前

最终要写一个 清除的一个函数

来销毁所有的单子 你如果忘了写它

那么这些对象就都没有被清除

就有可能导致问题

所以 我们使用一个新的结构来实现它

这个单子类和原来那个单子类

实现有很大的差别了

其实呢它的实现比原来的代码还短

但是它就能够解决我们原来的那些问题

一个classSingleton花括号对里面

同样有一个GetData函数

同样有无参数的构造函数Singleton

单参数的一个拷贝构造函数

和一个赋值构造函数operate=

(像这样一个存在的赋值操作符

它是一个赋值构造函数)

同样有一个析构函数

但是你看它的私有字段

我们只有验证数据a

没有指向单子类静态单子的一个指针

因为以前所有的构造和析构的问题

都是因为分配了一个动态内存区域所导致的

所以我们不要指针了

那我们这个单子在哪里实现呢

那么我们就要在Get那个函数里边

定义和实现它

所以我将在这个函数内部

定义一个静态的单子对象_s

使用它的无参数版本对它进行初始化

return _s 返回在这个Get函数内部

定义的静态局部变量_s

不管这个静态局部变量是定义在普通函数里

还是定义在成员函数里

所有的静态变量都将像全局变量一样

分配存储空间

在整个程序运行期间一直维持着不变

我们返还的就是_s

不管你调用多少次Get 返回的都是它

这不就是单子了吗

尤其你考虑到 我们的构造函数全私有的

还好几个没实现

那不就是一定是这个样子吗

你只能通过Get得到那个单子

Get得到的就是唯一的那个静态局部量_s吗

那不就是单子吗

所以我们Get这个函数返回的就是

staticSingleton的一个引用

所以当你按照这样的模式

定义我们的单子类的时候

使用的方式就可以按照下面这个结构:Singleton::Get()

得到那个单子对象的一个引用

然后“.GetData” 得到了那个对象

然后访问它的公开的成员函数 GetData

因为它是一个引用

所以实际上我们还可以定义一个引用的别名

叫Singleton&sing

我把sing定义成Singleton的一个引用

然后我把它初始化成Singleton::Get()

这个没有问题

因为这是一个引用的初始化动作

sing只是Singleton::Get所得到的

那个静态局部量的一个别名

我们并没有创建一个新的数据对象

所以这个初始化动作

既不会发生值的拷贝 也不会发生赋值

所有的动作都不会发生

就是原来这个staticSingleton _s

所以不会发生值的拷贝动作

既然不会发生这个动作

那么我们引用的就是那个唯一的单子量

所以以后就可以在这个sing引用的上面

调用我们的成员函数

可以在sing上面调用成员函数Getdata

这样的方式显然比你一开始写的

Singleton::Get().GetData() 这个方式要简单

因为如果你程序中频繁地需要

使用到这条语句的话

输出的代码量太多了

如果你可以在这一段里面

定义好这个sing这个引用

你可以一直在sing上面去访问它 去操作它

这样节省你编程的代码量

可以按照这个模式进行工作

虽说这是一个不太好的方式

如果真是一个单子

其实我建议同学们不要定义这样的量

即使是引用量 也不要定义它

每次都是Singleton::Get去得到它

这样的话 我们保证访问的就是

那个唯一的静态局部量

而且在源代码级别

能够看到它是完全唯一的

接下来就是静态常数据成员

它混合了常数据成员

和静态数据成员的两个特征

它的值不仅是在成员运行期间是不可以改变的

且每一个类只有唯一的一份

所以在定义的时候

要有static和const两个关键字

这样的量只能在类的外部进行初始化

我们看这个例子:

“classA { private: staticconstintcount; };”

我们要初始化它的时候

就constintA::count = 10

注意看 这里面有两个const

不仅在classA的里面

这个count数据成员的这个地方要写const

在这个数据成员的初始化的前面

也要写const 两个都不能省略

而static只需要在类的数据成员地方

写上就行了

并不需要在初始化的前边写上static

特别注意这一条

如果同学们在编程的时候会遗忘

觉得自己可能遗忘这个count是个static量的话

那么在定义并初始化这个count的时候

可以在const前边写上static 同时注释掉

这样的话一看就知道这是一个static量

如果你能记得 不写没关系

最后一个问题就是友元函数和友元类

友元是什么东西呢 就是朋友

有些特定的类在外界

有些函数或者另外一个类想使用它

不是想使用它的公开数据和公开成员

是想使用它的隐私数据和隐私成员

就说他是我好朋友 我对他呢 没秘密

我的秘密都会跟他说

一个类里面所有private量和private的函数

都是自己私有的 都是它秘密的东西

外界是看不见的 不能用

但是我的朋友呢 就想让它用

因为它需要频繁操作这些东西

(我这个类中的数据)

如果不给它用 太麻烦 不方便

所以我就想让它用

就意味着我对它没秘密

那么那些使用这个类的

私有数据成员或成员函数的

函数或类就是这个类的朋友

我们实现的机制就是friend这个关键字

你在一个函数的原型前面加上一个friend

你在一个类的声明的前面加上一个friend

就表示这个函数和这个类是这个类的友元类

注意这样的声明只能写在classCircle

这个类的定义里

你要在这个类的定义里写上frienddoubleGetRadius()

friendclassGlobe

就说明这个函数GetRadius和这个类Globe

就是Circle类的友元

一个是它的友元类 一个是它的友元函数

这个时候GetRadius和Globe

就可以访问Circle类的私有数据

radius就可以给它看了 它就能够访问了

这是非常非常重要的一个地方

注意 友元类和友元函数是要慎用的

为什么呢 因为它打破了

我们的类的数据封装以及信息隐藏的规则

因为外界的某个函数和某个类

可以自由地使用我们类中的

私有的数据或私有的函数

当然这个数据封装和信息隐藏

就不存在了嘛

所以用的时候要特别特别地小心

可是很多时候 在某些特定的场合下边

你不使用这个东西

编程实际上是极其不方便的

所以呢 我们其实又离不开它

注意 这个友元关系是不可逆的

Globe是Circle的朋友

Circle可不是Globe的朋友

同学们要特别注意这一点

Globe可以自由地访问Circle类的私有成员

并不意味着Circle可以自由地访问

Globe的私有的成员

除非它的那边也把我设成它的友元

双方互设友元 才能互相公开

友元是单向的 不可逆

特别注意这一条

还有友元这个写法不需要访问控制

它跟访问控制没关系

它只是一个声明 它不是类的成员

所以访问控制作用不到它的上面

不管你是写在private下边

还是public下面 还是protected下边

它都一样

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

LinuxCPP0908笔记与讨论

也许你还感兴趣的课程:

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