在看Apollo代码时发现一个写法奇怪的Singleton类,查了查发现很有来头
// @brief Thread-safe, no-manual destroy Singleton template
template <typename T>
class Singleton {
public:
// @brief Get the singleton instance
static T* get() {
pthread_once(&p_once_, &Singleton::new_);
return instance_;
}
private:
Singleton();
~Singleton();
// @brief Construct the singleton instance
static void new_() { instance_ = new T(); }
// @brief Destruct the singleton instance
// @note Only work with gcc
__attribute__((destructor)) static void delete_() {
typedef char T_must_be_complete[sizeof(T) == 0 ? -1 : 1];
(void)sizeof(T_must_be_complete);
delete instance_;
}
static pthread_once_t p_once_; // Initialization once control
static T* instance_; // The singleton instance
};
template <typename T>
pthread_once_t Singleton<T>::p_once_ = PTHREAD_ONCE_INIT;
template <typename T>
T* Singleton<T>::instance_ = nullptr;
单例模式
单例模式涉及一个单一的类,该类负责创建自己的对象,同时只允许一个对象存在。
对于一个全局使用的类可能会被频繁的创建和销毁时,使用单例模式来实现可以起到节省系统资源的目的。
比如配置文件的管理,就可以用单例实现。
实现思路
- 该类的构造函数定义为私有,这样其他地方的代码无法通过调用该类的构造方法来实例化;
类内提供一个静态方法,当调用该方法时,如果类的对象引用不为空则返回,如果为空则创建一个对象并引用。
《C++面试中的singleton类》里解释了为什么用引用而不是指针:因为Singleton返回的实例的生存期是由类本身决定的,而不是用户代码。一个简单实现
class Singleton
{
public:
static Singleton& Instance()
{
static Singleton singleton;
return singleton;
}
private:
Singleton() { };
};
在访问函数Instance()中将实例singleton声明为局部静态变量,以保证其
- 具有全局寿命
- 局部可见
- 只有第一次进入函数时才被初始化
这样做的一个好处是,实例只有在被调用时才创建(懒汉式),节约了系统资源
还有一种方式是,在构造函数中就创建实例(饿汉式),这种方式会提前抢占系统资源,但对多线程也是安全的(不会创建多个实例)
懒汉式多线程安全
当一个线程第一次调用Instance()时,会创建实例,假设在此时函数被另一个线程调用,又会生成一个实例。
class Singleton{
public:
static Singleton& Instance(){
if (singleton == NULL){
Lock lock;
if (singleton == NULL){
singleton = new Singleton();
}
return *singleton;
}
return *singleton;
}
private:
static Singleton* volatile singleton;
};
如上,使用双重检查的方式以保证线程安全
volatile修饰符是告诉编译器该变量可能会被未知原因(系统、线程等)的修改,不要对该处代码做优化。
单例类的重用
很显然,Singleton类是无法重用的,但可用模板实现Instance()的重用以达到目的
具体实现可以参考本文开头引用的Apollo代码