参考:http://blog.csdn.net/huhaijing/article/details/51756225
题目:
设计一个类,我们只能实现该类的一个实例。
单例模式:保证一个类只有一个实例,并提供一个访问它的全局访问点。
首先,需要保证一个类只有一个实例,在类中,要构造一个实例,就必须调用类的构造函数,如此,为了防止在外部调用类的构造函数而构造实例,需要将构造函数的访问权限标记为protected或private;最后,需要提供给全局访问点,就需要在类中定义一个static函数,返回在类内部唯一构造的实例。
解法1:只适用于单线程环境
// 剑指offer 面试题2 实现Singleton模式
#include <iostream>
using namespace std;
class Singleton
{
public:
static Singleton* getInstance()
{
// 在后面的Singleton实例初始化时,若后面是new Singleton(),则此处不必new;(废话)
// 若后面是赋值成NULL,则此处需要判断,需要时new
// 注意!然而这两种方式并不等价!后面的Singleton实例初始化时,new Singleton(),其实是线程安全的,因为static初始化是在主函数main()之前,那么后面的方法岂不是很麻烦。。。。这也是我测试的时候想到的
/*
if(m_pInstance == NULL)
{
m_pInstance = new Singleton();
}
*/
return m_pInstance;
}
static void destroyInstance()
{
if (m_pInstance != NULL)
{
delete m_pInstance;
m_pInstance = NULL;
}
}
private:
Singleton(){}
static Singleton* m_pInstance;
};
// Singleton实例初始化
Singleton* Singleton::m_pInstance = new Singleton(); // 前面不能加static,会和类外全局static混淆
// 单线程获取多次实例
void Test1(){
// 预期结果:两个实例指针指向的地址相同
Singleton* singletonObj = Singleton::getInstance();
cout << singletonObj << endl;
Singleton* singletonObj2 = Singleton::getInstance();
cout << singletonObj2 << endl;
Singleton::destroyInstance();
}
int main(){
Test1();
getchar();
return 0;
}
解法2:多线程可运行但效率低下
如果在同一时刻两个线程同时运行到if (m_Instance != NULL)
,此刻又没有创建实例,那么两个线程都会创建一个实例。为了保证在多线程环境下只生成一个实例,需要加上一个同步锁。
#include <iostream>
#include <mutex>
#include <thread>
#include <vector>
using namespace std;
class Singleton
{
private:
static mutex m_mutex; // 互斥量
Singleton(){}
static Singleton* m_pInstance;
public:
static Singleton* getInstance(){
if (m_pInstance == NULL){
m_mutex.lock(); // 使用C++11中的多线程库
if (m_pInstance == NULL){ // 两次判断是否为NULL的双重检查
m_pInstance = new Singleton();
}
m_mutex.unlock();
}
return m_pInstance;
}
static void destroyInstance(){
if (m_pInstance != NULL){
delete m_pInstance;
m_pInstance = NULL;
}
}
};
Singleton* Singleton::m_pInstance = NULL; // 所以说直接new 多好啊,可以省去Lock/Unlock的时间
mutex Singleton::m_mutex;
void print_singleton_instance(){
Singleton *singletonObj = Singleton::getInstance();
cout << singletonObj << endl;
}
// 多个进程获得单例
void Test1(){
// 预期结果,打印出相同的地址,之间可能缺失换行符,也属正常现象
vector<thread> threads;
for (int i = 0; i < 10; ++i){
threads.push_back(thread(print_singleton_instance));
}
for (auto& thr : threads){
thr.join();
}
}
int main(){
Test1();
Singleton::destroyInstance();
getchar();
return 0;
}
此处进行了两次m_pInstance == NULL的判断,是借鉴了Java的单例模式实现时,使用的所谓的“双检锁”机制。因为进行一次加锁和解锁是需要付出对应的代价的,而进行两次判断,就可以避免多次加锁与解锁操作,同时也保证了线程安全。但是,如果进行大数据的操作,加锁操作将成为一个性能的瓶颈;为此,一种新的单例模式的实现也就出现了。如果不进行两次判断,则每次调用static Singleton* getInstance()都需要先加锁,然后再判断,这样效率更加低下。
解法3:const static 型实例
#include <iostream>
#include <thread>
#include <vector>
using namespace std;
class Singleton
{
private:
Singleton(){}
static const Singleton* m_pInstance; //这里和之前的不同
public:
static Singleton* getInstance(){
return const_cast<Singleton *>(m_pInstance); // 去掉“const”特性
// 注意!若该函数的返回值改为const static型,则此处不必进行const_cast静态转换
// 所以该函数可以改为:
/*
const static Singleton* getInstance(){
return m_pInstance;
}
*/
}
static void destroyInstance(){
if(m_pInstance != NULL){
delete m_pInstance;
m_pInstance = NULL;
}
}
};
const Singleton* Singleton::m_pInstance = new Singleton(); // 利用const只能定义一次,不能再次修改的特性,static继续保持类内只有一个实例
void print_singleton_instance(){
Singleton *singletonObj = Singleton::getInstance();
cout << singletonObj << endl;
}
// 多个进程获得单例
void Test1(){
// 预期结果,打印出相同的地址,之间可能缺失换行符,也属正常现象
vector<thread> threads;
for(int i = 0; i < 10; ++i){
threads.push_back(thread(print_singleton_instance));
}
for(auto& thr : threads){
thr.join();
}
}
int main(){
Test1();
Singleton::destroyInstance();
return 0;
}
解法4:实现按需创建实例
#include <iostream>
#include <thread>
#include <vector>
using namespace std;
class Singleton
{
private:
Singleton(){}
public:
static Singleton* getInstance(){
static Singleton m_pInstance; // 注意,声明在该函数内
return &m_pInstance;
}
};
void print_singleton_instance(){
Singleton *singletonObj = Singleton::getInstance();
cout << singletonObj << endl;
}
// 多个进程获得单例
void Test1(){
// 预期结果,打印出相同的地址,之间可能缺失换行符,也属正常现象
vector<thread> threads;
for(int i = 0; i < 10; ++i){
threads.push_back(thread(print_singleton_instance));
}
for(auto& thr : threads){
thr.join();
}
}
// 单个进程获得多次实例
void Test2(){
// 预期结果,打印出相同的地址,之间换行符分隔
print_singleton_instance();
print_singleton_instance();
}
int main(){
cout << "Test1 begins: " << endl;
Test1();
cout << "Test2 begins: " << endl;
Test2();
return 0;
}
其实自己还有好多地方没太明白,先写在这里吧。
网友评论