单例模式
单例模式是最简单的一种设计模式,单例模式的主要作用是保证全局只有一个该类的实例,这在很多工程中特别重要,比如一些工具类我们就可以使用单例模式实现。
为了保证全局只有一个实例,我们首先需要将该类的构造函数声明为私有,这样就无法从外部创造他的实例了。然后在类中声明一个静态指针变量保存唯一实例的地址,最后在设计一个public的get函数来使得外部可以获得实例。
class Singleton {
private:
static shared_ptr<Singleton> m_instance;
Singleton() = default;
public:
static shared_ptr<Singleton>& getInstance() {
if (m_instance == nullptr) {
m_instance = shared_ptr<Singleton>(new Singleton); // 不可以使用make_shared
}
return m_instance;
}
void saySomething() {
cout << "Singleton Pattern Data : " << endl;
}
};
shared_ptr<Singleton> Singleton::m_instance;
添加锁
但是很明显上面的实现在多线程下会出错,假设我们现在有A,B两个线程。
线程 A | 线程 B |
判断 if (m_instance) 得到结果 m_instance 为空 | |
判断 if (m_instance) 得到结果 m_instance 为空 | |
m_instance = shared_ptr<Singleton>(new Singleton); | |
m_instance = shared_ptr<Singleton>(new Singleton); 重复生成类的实例,这样就有了两个类实例 如果没有使用智能指针,这里可能还存在内存泄漏问题 |
所以在多线程下需要加锁保证线程安全
class Singleton {
private:
static shared_ptr<Singleton> m_instance;
static pthread_mutex_t mutex;
Singleton() = default;
public:
static shared_ptr<Singleton>& getInstance() {
pthread_mutex_lock(&mutex);
if (m_instance == nullptr) {
m_instance = shared_ptr<Singleton>(new Singleton); // 不可以使用make_shared
}
pthread_mutex_unlock(&mutex);
return m_instance;
}
void saySomething() {
cout << "hey, i am a Singleton class " << endl;
}
};
shared_ptr<Singleton> Singleton::m_instance;
pthread_mutex_t Singleton::mutex;
双判断加锁
但是每次加锁解锁以及线程被锁定时的等待时间过长引起了访问效率的问题,这时候怎么办呐
在加锁前和加锁后各添加一次判断:
class Singleton {
private:
static shared_ptr<Singleton> m_instance;
static pthread_mutex_t mutex;
Singleton() = default;
public:
static shared_ptr<Singleton>& getInstance() {
if (m_instance == nullptr) {
pthread_mutex_lock(&mutex);
if (m_instance == nullptr) {
m_instance = shared_ptr<Singleton>(new Singleton); // 不可以使用make_shared
}
pthread_mutex_unlock(&mutex);
}
return m_instance;
}
void saySomething() {
cout << "hey, i am a Singleton class " << endl;
}
};
shared_ptr<Singleton> Singleton::m_instance;
pthread_mutex_t Singleton::mutex;
需要注意的问题
- 类成员变量一定要在类内声明并且在类外定义
- 单例模式的类的构造函数要声明为private或者protected
- make_shared 函数需要调用拷贝构造函数,而单例模式中的拷贝函数是不可以被调用的,所以在单例模式下面不可以使用该函数为智能指针申请内存空间