单例的实现在实现一个单例时,需要从如下角度来考虑单例的实现是否合理: 饿汉式懒汉式单例从生命周期来讲,是“与天地同寿的”,在调用main函数之前,单例的构造函数就已经被调用必完成了初始化。(在msvc C++中,真正的函数入口是msvcStartup而不是main函数,在调用main函数之前, //Signleton.h class Signleton { public: virtual ~Signleton(); Signleton& Getinstance(); private: Signleton();//先藏起构造函数 }; //Signleton.cpp Signleton instance;//这个是放在cpp文件里面的全局变量。 Signleton& Signleton::Getinstance() { return instance;//不需要加锁因为在系统构造前就建好了实例 } 饿汉式由于是在系统启动之前就存在了,显然是不支持延迟加载的。与此同时,由于先于系统启动,也不需要 懒汉式懒汉式恰恰和饿汉式相反,生存周期可以说是“应运而生”,就是说只有在第一次调用Getinstance时才会被实例化。具体实现如下: //Signleton.h class Signleton { public: virtual ~Signleton(); Signleton& Getinstance(); private: Signleton();//先藏起构造函数 private: Signleton* mInstance; }; //Signleton.cpp Signleton* Signleton::mInstance = nullptr; Signleton& Signleton::Getinstance() { if (nullptr == mInstance) { mInstance = new Signleton; } return *mInstance; } 那么,还是使用你是三个问题来评判懒汉式的实现: 双重检查式为了满足在多线程环境下运行的需求,现在对懒汉式实现打点补丁。具体实现如下: //Signleton.h class Signleton { public: virtual ~Signleton(); Signleton& Getinstance(); private: Signleton();//先藏起构造函数 private: Signleton* mInstance; mutex mMutex; }; //Signleton.cpp Signleton* Signleton::mInstance = nullptr; Signleton& Signleton::Getinstance() { if (nullptr == mInstance) {//注意这里是判断了两次 mMutex.lock(); if (nullptr == mInstance) { mInstance = new Signleton; } mMutex.unlock(); } return *mInstance; } 打了补丁之后的懒汉式单例,既可以支持延时加载,又可以在多线程环境下运行。在调用 Signleton& Signleton::Getinstance() { mMutex.lock(); if (nullptr == mInstance) { mInstance = new Signleton; } mMutex.unlock(); return *mInstance; } 这样的话,每次调用Getinstance都会加锁解锁,开销很大,降低了系统效率。 Signleton& Signleton::Getinstance() { if (nullptr == mInstance) { mMutex.lock(); mInstance = new Signleton; mMutex.unlock(); } return *mInstance; } 其实也不行,这样也会存在同时多个线程通过 局部变量式从效果上来看,双重加锁仿佛已经是无敌了,及支持延时加载,也可以支持多线程并发,同时加锁也仅仅是在第一次调用,效率较高。还有一种写法,原理上和双重加锁类似,但是更加简洁,具体实现如下: Signleton& Signleton::Getinstance() { static Singleton value; //静态局部变量 return value; } 上面的式子,利用了局部静态变量在第一次被调用时就会初始化的特点,而且C++11标注明确规定了静态变量的实例化是线程安全的,所以说只有第一次加载且线程安全这两个条件,都利用C++自身的特性实现了,所以我说思想和双重检测类似,但是写法更简单。 |
|