C++11标准下的单例设计模式

Posted AllenSquirrel

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++11标准下的单例设计模式相关的知识,希望对你有一定的参考价值。

单例设计模式

设计模式(Design Pattern)是一套被反复使用、多数人知晓的、经过分类的、代码设计经验的总结。

为什么会产生设计模式这样的东西呢?

使用设计模式的目的:为了代码可重用性、让代码更容易被他人理解、保证代码可靠性。 设计模式使代码编写真正工程化;设计模式是软件工程的基石脉络,如同大厦的结构一样。

其中最具代表性的就是单例设计模式

单例设计模式

一个类只能创建一个对象,即单例模式,该模式可以保证系统中该类只有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享。比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息,这种方式简化了在复杂环境下的配置管理。

在linux下曾提到过单例设计模式:

实现:

  • 饿汉模式:资源在程序初始化阶段就完成加载(空间换时间)
  • 饿汉方法实现:
  1. 采用静态方法修饰资源,所有对象共用同一份资源,程序初始化阶段完成资源加载且只被加载一次
  2. 构造函数私有化 ,保证一个类只能实例化一个对象
template <typename T>
class Singleton {
    static T data;
    Singleton(){}//构造函数私有化
public:
    static T* GetInstance() {
        return &data;
    }
};
  • 懒汉模式:资源在使用的时候再去加载(延迟加载)
  1. 定义对象指针,初始资源为空
  2. static修饰,共用一份资源
  3. volatite修饰 ,防止编译器过度优化
  4. 加锁保护  线程安全
  5. 二次检测,防止锁冲突
template <typename T>
class Singleton {
    volatite static T* inst;//定义对象指针  初始为空
    static std::mutex _mutex;
public:
    static T* GetInstance() {
        if(inst==NULL)//double check 二次检测,避免不为空情况依然加锁,造成锁冲突
        {
            _mutex.lock();
            if (inst == NULL) {
                inst = new T();//调用时发现为空,进行获取资源
            }
            _mutex.unlock();
        }
        return inst;
    }
};

总结来说,单例设计主要分为两种:

  • 饿汉模式(提前响应)
  • 懒汉模式(延迟加载)
  • 饿汉模式

在C++11标准下,饿汉模式要求:程序启动时就创建一个唯一的实例对象

要满足此条件,提前创建对象,只有将其定义为全局变量或静态变量,才会程序启动前完成创建

// 饿汉模式
// 优点:简单
// 缺点:可能会导致进程启动慢,且如果有多个单例类对象实例启动顺序不确定。
class Singleton
{
public:
static Singleton* GetInstance()
{
return &m_instance;
}
private:
// 构造函数私有
Singleton(){};

// C++11防拷贝
Singleton(Singleton const&) = delete;
Singleton& operator=(Singleton const&) = delete;
static Singleton m_instance;
};
Singleton Singleton::m_instance; // 在程序入口之前就完成单例对象的初始化

注:
1,构造函数私有  外部无法创建对象
2,通过接口  指针获取已创建对象
3,类内 定义静态成员对象,类外初始化

4,定义静态成员,静态成员定义在类内,类内可以访问私有成员函数完成构造,且静态成员存储在静态数据区,与类不为同一块存储空间

  • 懒汉模式

#include <iostream>
#include <mutex>
#include <thread>
using namespace std;
class Singleton
{
public:
    static Singleton* GetInstance() {
// 注意这里一定要使用Double-Check的方式加锁,才能保证效率和线程安全
    if (nullptr == m_pInstance) {//第一次判断防止多次加锁导致锁冲突
        m_mtx.lock();
        if (nullptr == m_pInstance) {//第二次判断是否为空,为空则为第一次调用构造函数创建,不为空,后续都不会调用构造函数,满足对象第一次创建且仅创建一次
            m_pInstance = new Singleton();
        }
        m_mtx.unlock();
    }    
    return m_pInstance;
    }
    // 实现一个内嵌垃圾回收类
    class CGarbo {
    public:
        ~CGarbo(){
            if (Singleton::m_pInstance)
                delete Singleton::m_pInstance;
        }
    };
// 定义一个静态成员变量,程序结束时,系统会自动调用它的析构函数从而释放单例对象
static CGarbo Garbo;
private:
    // 构造函数私有
    Singleton(){};
    // 防拷贝
    Singleton(Singleton const&);
    Singleton& operator=(Singleton const&);
    static Singleton* m_pInstance; // 单例对象指针 静态成员变量,在调用构造函数前先创建进行判断
    static mutex m_mtx; //互斥锁
};
Singleton* Singleton::m_pInstance = nullptr;//静态成员变量类外初始化,第一次初始化为nullptr
Singleton::CGarbo Garbo;
mutex Singleton::m_mtx;
void func(int n)
{
    cout<< Singleton::GetInstance() << endl;
}

// 多线程环境下演示上面GetInstance()加锁和不加锁的区别。
int main()
{
    thread t1(func, 10);
    thread t2(func, 10);
    t1.join();
    t2.join();
    cout << Singleton::GetInstance() << endl;
    cout << Singleton::GetInstance() << endl;
}

 

以上是关于C++11标准下的单例设计模式的主要内容,如果未能解决你的问题,请参考以下文章

多线程下的单例

多线程下的单例模式详解

多线程下的单例模式详解

Unity3D中的单例模式

多线程下的单例模式

性能比较好的单例写法