版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、中C+单例实现问题分析_ 方案一 代码如下: class QMManager public: static QMManager instance() static QMManager instance_; return instance_; 这是最简洁的版本,在单线程下(或者是C+0X下)是没任何问题的,但在多线程下就不行了,由于static QMManager instance_;这句话不是线程平安的。 在局部作用域下的静态变量在编译时,编译器会创建一个附加变量标识静态变量是否被初始化,会被编译器变成像下面这样(伪代码): 代码如下: static QMManager instance()
2、static bool constructed = false; static uninitialized QMManager instance_; if (!constructed) constructed = true; new(s) QMManager; /construct it return instance_; 这里有竞争条件,两个线程同时调用instance()时,一个线程运行到if语句进入后还没设constructed值,此时切换到另一线程,constructed值还是false,同样进入到if语句里初始化变量,两个线程都执行了这个单例类的初始化,就不再是单例了。 方案二 一个
3、解决方法是加锁: 代码如下: static QMManager instance() Lock(); /锁自己实现 static QMManager instance_; UnLock(); return instance_; 但这样每次调用instance()都要加锁解锁,代价略大。 方案三 那再转变一下,把内部静态实例变成类的静态成员,在外部初始化,也就是在include了文件,main函数执行前就初始化这个实例,就不会有线程重入问题了: 代码如下: class QMManager protected: static QMManager instance_; QMManager(); QM
4、Manager(); public: static QMManager *instance() return instance_; void do_something(); ; QMManager QMManager:instance_; /外部初始化 这被称为饿汉模式,程序一加载就初始化,不管有没有调用到。 看似没问题,但还是有坑,在一个2B状况下会有问题:在这个单例类的构造函数里调用另一个单例类的方法可能会有问题。 看例子: 代码如下: /.h class QMManager protected: static QMManager instance_; QMManager(); QMMan
5、ager(); public: static QMManager *instance() return instance_; ; class QMSqlite protected: static QMSqlite instance_; QMSqlite(); QMSqlite(); public: static QMSqlite *instance() return instance_; void do_something(); ; QMManager QMManager:instance_; QMSqlite QMSqlite:instance_; /.cpp QMManager:QMMan
6、ager() printf(QMManager constructorn); QMSqlite:instance()-do_something(); QMSqlite:QMSqlite() printf(QMSqlite constructorn); void QMSqlite:do_something() printf(QMSqlite do_somethingn); 这里QMManager的构造函数调用了QMSqlite的instance函数,但此时QMSqlite:instance_可能还没有初始化。 这里的执行流程:程序开头后,在执行main前,执行到QMManager QMManag
7、er:instance_;这句代码,初始化QMManager里的instance_静态变量,调用到QMManager的构造函数,在构造函数里调用QMSqlite:instance(),取QMSqlite里的instance_静态变量,但此时QMSqlite:instance_还没初始化,问题就消失了。 那这里会crash吗,测试结果是不会,这应当跟编译器有关,静态数据区空间应当是先被安排了,在调用QMManager构造函数前,QMSqlite成员函数在内存里已经存在了,只是还未调到它的构造函数,所以输出是这样: QMManager constructor QMSqlite do_somethi
8、ng QMSqlite constructor 方案四 那这个问题怎么解决呢,单例对象作为静态局部变量有线程平安问题,作为类静态全局变量在一开头初始化,有以上2B问题,那结合下上述两种方式,可以解决这两个问题。boost的实现方式是:单例对象作为静态局部变量,但增加一个帮助类让单例对象可以在一开头就初始化。如下: 代码如下: /.h class QMManager protected: struct object_creator object_creator() QMManager:instance(); inline void do_nothing() const ; static obje
9、ct_creator create_object_; QMManager(); QMManager(); public: static QMManager *instance() static QMManager instance; return instance; ; QMManager:object_creator QMManager:create_object_; class QMSqlite protected: QMSqlite(); QMSqlite(); struct object_creator object_creator() QMSqlite:instance(); inl
10、ine void do_nothing() const ; static object_creator create_object_; public: static QMSqlite *instance() static QMSqlite instance; return instance; void do_something(); ; QMManager:object_creator QMManager:create_object_; QMSqlite:object_creator QMSqlite:create_object_; 结合方案3的.cpp,这下可以看到正确的输出和调用了: QM
11、Manager constructor QMSqlite constructor QMSqlite do_something 来看看这里的执行流程: 初始化QMManager类全局静态变量create_object_ -调用object_creator的构造函数 -调用QMManager:instance()方法初始化单例 -执行QMManager的构造函数 -调用QMSqlite:instance() -初始化局部静态变量QMSqlite instance -执行QMSqlite的构造函数,然后返回这个单例。 跟方案三的区分在于QMManager调用QMSqlite单例时,方案3是取到全局静
12、态变量,此时这个变量未初始化,而方案四的单例是静态局部变量,此时调用会初始化。 跟最初方案一的区分是在main函数前就初始化了单例,不会有线程平安问题。 最终boost 上面为了说明清晰点去除了模版,实际用法是用模版,不用写那么多重复代码,这是boost库的模板实现: 代码如下: template typename T struct Singleton struct object_creator object_creator() SingletonT:instance(); inline void do_nothing()const ; static object_creator create
13、_object; public: typedef T object_type; static object_type instance() static object_type obj; /据说这个do_nothing是确保create_object构造函数被调用 /这跟模板的编译有关 create_object.do_nothing(); return obj; ; template typename T typename SingletonT:object_creator SingletonT:create_object; class QMManager protected: QMManager(); QMManag
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2026年护理质控培训模拟试题(含答案)
- 2026年护理学导论考查模拟试题(含答案)
- 2026年茶艺师模拟试题(含答案)
- 2026年申论a类历年模拟题及答案详解
- 2026年土木工程检测模拟题及答案详解
- 2026年内部控制模拟题及答案详解
- 2026年费用类模拟试卷(含答案)
- 2026年电大物流模拟试卷(含答案)
- 2026年环保局环保模拟试题(含答案)
- 2026年安徽操作系统模拟试题(含答案)
- 贵州省公路占道作业安全技术指南(试行)
- 头发移植课件
- 《深度学习与神经网络》全套教学课件
- GB/T 9535.2-2025地面用光伏组件设计鉴定和定型第2部分:试验程序
- 煤矿安全生产风险分级管控和隐患排查治理双重预防体系建设培训课件
- 全国民用爆炸物品信息管理系统领用发放手持机用户手册
- Colles骨折课件教学课件
- 委托检测服务合同协议书
- 长期总代理协议书范本
- 大学生职业规划大赛成长赛道
- NB-T+33008.1-2018电动汽车充电设备检验试验规范 第1部分:非车载充电机
评论
0/150
提交评论