台湾人的,实作
是实现
的意思.
很复杂的.
包括如何分配
,如何消灭
,多线程安全
,消灭后操作
.
静态函数/成员变量的缺点:不能为虚函数
,外界很难改变行为.并且很难初化/清理
.
元<型名 T,
元<类>类 创建策略=用新创建,
元<类>类 生命期策略=默认生命期,
元<类,类>类 线程模型=洛基默认线程无对象级,
类 互斥锁策略=洛基默认互斥锁
>
类 单件持有者{
公:
又 T 对象类型;
静 T&实例();
私:
静 空 造实例();
静 空 洛基限定c调用传统 消灭单件();
单件持有者();
又 型名 线程模型<T*,互斥锁策略>::易失类型 针实例类型;
静 针实例类型 p实例_;
静 极 消灭_;
};
//这样实例化.
{
如(!p实例_){造实例();}
中*p实例_;//静态初化.
}//传回引用较安全,避免被`删`.
将拷贝/复制构造
声明为私/删
.禁用.析构函数声明为私
.用消灭
函数来消灭
.
消灭时或者利用局部静态变量
.要区别执行期初化
与编译期常量初化
(运行前就完成赋值).利用的是退出时()
.c运行时程序结束时运行.
单件模式特征:易于表达/理解,但难以实现
.
局部静态
单件,不一定正确.如键盘,显示,日志
.
键盘成功,显示失败,则日志跟随失败.则是死引用.因为日志也是单件.此时键盘再失败,则无法记录.因为日志已被消灭了.希望日志无论何时创建,但得在(键盘,显示)之后消灭.一个函数一个职责
.现在要增加检测死引用
,负责处理错误.增加一个已消灭
变量.如果寿命更长对象取这个单件
,则到达死引用时
,并抛异常.
进一步,不死鸟
来了.检测死引用
时复活
.因为静态变量内存
在整个程序周期都存在.在原地复活.新(针)单件
.然后在退出时(注册消灭)
退出时修复
.修复退出时
问题.要定义#修复退出时
.
不然,要出问题.
死引用.带寿命的单件
.不死鸟,如有状态
,则可能未保存
状态.
生命期控制.现在的rust
就有.置寿命
,感觉就是自己当析构管家
,管理析构顺序
,只针对用新
分配的对象.
或者进行依赖管理
.但也麻烦,会产生循环依赖.
寿命法则:A依赖B,则A比B短命.所以不要依赖.
带寿命单件
,用类似优先队
,但寿命值相同时,先进后出
.
多线程
更麻烦.双检测
是错误的解法.锁很贵
,最后可能得靠原子.
空 单件持有者<T,创建策略,
生命期策略,线程模型,互斥锁策略>::造实例()
{
型名 线程模型<单件持有者,互斥锁策略>::锁 警卫;
(空)警卫;
如(!p实例_)
{
如(消灭_)
{
消灭_=假;
生命期策略<T>::死引用时();
}//死引用时
p实例_=创建策略<T>::创建();
生命期策略<T>::计划析构(p实例_,
&消灭单件);
}
}
现在已有独针,共针,弱针
.具有两个操作->
与*
操作.灵针具有值语义
(即可以复制),而指针没有.
有移动,共享(引用计数),总是复制
指针,
拥有权与构造/复制/析构
关系很大.
不要写死代码.降低通用性.如处理非标__near,__far,__huge
时.如果有别人的灵针
想对其升级时,将这个灵针
包装在自己的灵针
中,而不是继承他.
当调用->
时,类型非内置
类型,编译器不断->
直到达到内置指针
,再访问成员.因而灵针
可以不传指针
,而传有->方法
的对象
前调用,后调用
,假定按值从->操作返回一个指针对象
,然后:
1,构造指针对象
.
2,调用指针对象
的->
,返回被指对象
指针.
3,访问被指对象的->
,执行实际动作.
4,析构指针对象
.
在多线程及访问资源时有用,叫锁定函数调用
,可在构造函数
中锁定资源
,析构函数中
解除锁定.
句柄
防止用户直接操作资源
.是隐藏表格的指针索引
此时,提供->/*
操作没意义.
因而,提出被指类型,存储类型,引用类型
三种存储类型
作为一种策略
.灵针
不适合用成员函数
.因为会混淆
灵针/被指对象的调用.
如打印机
也有获取/释放
.光靠->/.
来区别是不现实的.因而灵针
只使用非成员函数
,通过友
来解决.
灵针需要保留,构造,析构,->,=,*,
函数,其余由命名非成员函数提供.
拥有权管理
最重要.如移动,共享
.
一种是,只管复制,比按值
传递多的好处就是多态
.深复制
,用虚复制
.具体复制时,返回子对象的基针
,靠策略.
一是写时复制
.但这个写时复制
无法区分被指对象的常与非常成员函数调用
.灵针
太低级,写时复制
很高级了.不过,实现写时复制
时,可以用灵针
.
引用计数,最普遍,最有效.这些都是小对象,最好用小对象分配器
不然,分配太慢,如果通过间接层,则访问时又太慢.最好将计数
通过被指
对象保存起来.这是侵入式
计数.非侵入式
,最好也用小对象分配器
.
还有引用链
,双向引用链
,插入,删除,空检测
都是常数时间.引用链
空间花费少,时间多.一般不用.
引用计数还有个问题,就是循环引用
,这就是弱针
上场时.
移动
指针.转移所有权.还可以重载&
操作符.取址.但不好.暴露了地址,放弃了自动管理.且不能使用stl
了.因为多数代码假定用&
返回T*
.还可以隐式转为原始指针
,可以通过显式转换使用.如.取()
得到原始指针.
采取完整可靠操作,重载每个操作符.
还有,就是灵针<基>
与灵针<继>
之间的转换.重载==
与!=
为相应的模板类.
元<型名 T1,元<类>类 操作1,类 CP1,元<类>类 KP1,元<类>类 SP1,元<类>类 CNP1>
极 符号==(常 灵巧针<T1,操作1,CP1,KP1,SP1,CNP1>&右边)常
{中 取实现(*本)==取实现(右边);}
由用户来实现符号<
以达到排序
功能.借助标::较小
可以实现.同一程序,可能需要高安/低速
或高速/低安
.
检测分初化检测/提前检测
.如不能为空针
,但有时空针又有用.解引用时
要检查,检查一次后,以后就不检查了.安全/高效
.再一个是错误报告/错误策略
,
双常性,指针的常
,被指
的常
.
数组.如果灵针<数组>
,则需要记得删[]
,此数组非现在数组
.
多线程.被指对象锁定,加个间接层,构造函数中加个锁.借助->
不断的->
.访问后就析构间接层
,释放锁了.
双链
结构,都得加锁.将多线程加入所有者策略
中.
类级锁定
锁定某个类的所有对象.对象级
锁定,只锁定对象相关操作
.前者慢,占用内存少.后者快,占用内存多.多线程安全
的引用计数,需要原子操作,多线程的引用链接
则需要锁
.原子操作相对来说,要便宜一点
.
每一个可变点
转为策略
.
组合起来:存储,所有者,转换,检查
策略.
把一个策略当作大函数
,则什么都明白了.策略类直接作为模板的参数
.一般,最常定制的策略放在最前.所有策略要具值语义
:(复制构造/赋值)函数.