## 摘要
在《more effective C 》中,作者曾在限制类所能生成对象的个数章节讨论过“允许产生0个或1个对象”,其实该部分讲解的方法就是单例模式。而单例模式的底层思路就是:禁止用户自己定义对象,通过定义方法给用户调用来生成对象。
## 定义
要求一个类只能生成一个对象,且整个软件体系内对于该对象的依赖程度相同。
**单个实例可以减少内存开支,如果对象的构造需要资源时还可以减少资源的占用**
## 实现方式
单例模式的实现有常用的“饿汉模式”和“懒汉模式”。
> 所谓懒汉模式:只在初次调用该实例化函数时才创建对象,意指懒。
> 所谓饿汉模式:在main函数执行之前就已经将实例化对象构造完成,意指饿。
## 两者对比
饿汉模式没有线程安全问题,但是懒汉模式却存在线程安全问题,因此针对懒汉模式需要double-check,
## 懒汉模式实现1
```cpp
//.h文件
#include<mutex>
class Singlton
{
public:
static Singlton* instance();
private:
Singlton() = default;
Singlton(const Singlton& sing) = default;
Singlton& operator=(const Singlton& sing) = default;
private:
static Singlton* m_instance;
static std::mutex m_mutex;
};
//.cpp文件
#include "Singlton.h"
Singlton* Singlton::m_instance{ nullptr };//未实例化对象
std::mutex Singlton::m_mutex;
Singlton* Singlton::instance()
{
if (m_instance == nullptr)
{
std::unique_lock<std::mutex> lk(m_mutex);
if (m_instance == nullptr)
{
m_instance = new Singlton;
}
lk.unlock();
}
return m_instance;
}
```
**在获得实例的方法instance中,采用double-check的方法,所以该实现方法是线程安全且锁的区域也是合理的**
## 饿汉模式实现2
```cpp
//采用智能指针 call_once
class Singleton {
private:
Singleton() {
std::cout << "Constructor called!" << std::endl;
}
static void createInstance() {
m_instance_ptr = std::shared_ptr<Singleton>(new Singleton);
}
public:
typedef std::shared_ptr<Singleton> Ptr;
~Singleton() {
std::cout << "Destructor called!" << std::endl;
}
static std::shared_ptr<Singleton> get_instance() {
static std::once_flag s_flag;
//std::call_once(s_flag, createInstance);
std::call_once(s_flag, [] { m_instance_ptr = std::shared_ptr<Singleton>(new Singleton); });
return m_instance_ptr;
}
void use() const { std::cout << "in use" << std::endl; }
private:
static std::shared_ptr<Singleton> m_instance_ptr;
};
//静态成员变量初始化
Singleton::Ptr Singleton::m_instance_ptr = nullptr;
```
## 改进版的饿汉模式1
```cpp
m_instance = new Singlton;
```
当用new创建对象其实分为了三个步骤:
> 调用operator new分配内存
> 在分配的空间上调用构造函数完成对象构造
> 让m_instance指向分配的空间
虽然如上进行了DCL,但是当存在编译器优化或运行时优化,则有可能导致第二步、第三步乱序,此时m_instance获得的是未构造完成的对象。基于此,可以通过原子变量(atomic<T>)进行优化。
## 改进版的饿汉模式2
采用静态局部变量的形式(C 大师的方法)
```cpp
class Singleton {
public:
~Singleton() {
std::cout << "Destructor called!" << std::endl;
}
Singleton(const Singleton &) = delete;
Singleton &operator=(const Singleton &) = delete;
static Singleton &get_instance() {
static Singleton instance;
return instance;
}
void use() const { std::cout << "in use" << std::endl; }
private:
Singleton() {
std::cout << "Constructor called!" << std::endl;
}
};
```
## 饿汉模式实现
```cpp
//singleton.h
class singleton{
private:
singleton(){}
static singleton* p;
public:
static singleton* instance();
int a;
};
//singleton.cpp
singleton* singleton::p = new singleton();
singleton* singleton::instance()
{
return p;
}
```
实例作为静态成员会在进入main函数前进行实例化,所以不会具有线程安全问题