总结单例模式在C++下的实现
1. 介绍
单例模式, 是23种GOF设计模式之一, 属于创建型. 顾名思义, 单例模式就是一个类只能实例化出一个对象.
考虑两个问题
1.1 如何保证只能实例出一个对象?
- 将构造函数设为private, 防止在类外生成栈对象/堆对象
- 将赋值运算符设为private, 防止赋值对象(上面构造函数为private已经间接确保了不可能赋值).
1.2 外界如何获取单例?
- 提供public的静态方法getInstance()
- 保证线程安全
2. 实现方法
2.1 饿汉式
饿汉式, 就是不管是否用到, 默认都会创建.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| class SingletonEager { public: static SingletonEager & getInstance() { return m_instance; }
private: static SingletonEager m_instance;
SingletonEager() { std::cout << __FUNCTION__ << " is caled. " << std::endl; }
~SingletonEager() { std::cout << __FUNCTION__ << " is caled. " << std::endl; }
SingletonEager(const SingletonEager & s); SingletonEager& operator =(const SingletonEager & s); };
SingletonEager SingletonEager::m_instance;
|
两点说明:
- 这里使用类的静态变量来实现. 静态对象在main()前被构造执行初始化, 具体可参考C++的全局对象.
- 但存在一个问题: 如果该类被另一个全局对象/静态对象使用, 但这两个类不在同一源文件(不同的编译单元), 则可能存在问题. 因为不同编译单元的non-local对象的初始化顺序是不确定的.
2.2 懒汉式
懒汉式, 就是第一次调用到的时候才去创建.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| class SingletonLazy { public: static std::unique_ptr<SingletonLazy> & getInstance() { std::lock_guard<std::mutex> lck(m_mtx); if(m_instance == nullptr) { m_instance.reset(new SingletonLazy()); }
return m_instance; }
~SingletonLazy() { std::cout << __FUNCTION__ << " is caled. " << std::endl; }
private: static std::mutex m_mtx; static std::unique_ptr<SingletonLazy> m_instance;
SingletonLazy() { std::cout << __FUNCTION__ << " is caled. " << std::endl; }
SingletonLazy(const SingletonLazy & s); SingletonLazy& operator =(const SingletonLazy & s); };
std::mutex SingletonLazy::m_mtx; std::unique_ptr<SingletonLazy> SingletonLazy::m_instance;
|
两点说明
- 使用智能指针, 保证程序退出前堆对象能正确释放
- getInstance()需要加锁, 用于保证多线程安全
2.3 局部静态对象
Effective C++一书中item4(确保对象初始化)中, 提到了Scott Meyer版本的实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| // local static class SingletonLocalStatic { public: static SingletonLocalStatic & getInstance() { static SingletonLocalStatic instance; return instance; }
private: SingletonLocalStatic() { std::cout << __FUNCTION__ << " is caled. " << std::endl; }
~SingletonLocalStatic() { std::cout << __FUNCTION__ << " is caled. " << std::endl; }
SingletonLocalStatic(const SingletonLocalStatic & s); SingletonLocalStatic& operator =(const SingletonLocalStatic & s); };
|
C++11/较新的gcc编译器, 能保证局部静态对象的线程安全, 具体实现原理可参考之前研究过的C++的局部静态对象
3. 总结
实现方式 |
说明 |
饿汉式 |
若被其它编译单元的全局对象/静态对象使用, 有可能存在初始化顺序问题 |
懒汉式 |
加锁影响性能 |
局部静态对象 |
C++11/gcc较新版本才支持, 推荐使用 |
程序员自我修养(ID: dumphex)