单例模式C++


单例模式

用visual studio 2019测试


一、单例模式

1.定义

单例模式指在整个系统生命周期里,保证一个类只能产生一个实例,确保该类的唯一性。

2.分类

懒汉式:

指系统运行中,实例并不存在,只有当需要使用该实例时,才会去创建并使用实例。(这种方式要考虑线程安全)

饿汉式:

指系统一运行,就初始化创建实例,当需要时,直接调用即可。(本身就线程安全,没有多线程的问题)

3.注意

为了保证单例,构造函数、析构函数、拷贝构造函数、赋值运算符重载函数均要定义成私有

普通的懒汉模式是非线程安全的,因为单例对象是在GetInstance()函数中实例化的,如果多个线程同时调用GetInstance()函数,则可能会实例化多个单例类,这就违背了单例模式的初衷了;

饿汉模式本身就是安全了,因为单例对象是跟随类的定义实例化的,所以一定是唯一的;

为了解决懒汉模式非线程安全的问题,需要在GetInstance()函数中实例化对象时加锁;

为什么不直接在单例类的析构函数中释放静态对象,而要弄一个内部类来释放静态对象?

只有在delete对象时候,才会调用析构函数,delete对象却是在析构函数中执行的,根本进不去析构函数,这就陷入了僵局;

二、懒汉式单例 ( 线程不安全 )

#include <iostream> // std::cout
#include <mutex>    // std::mutex
#include<chrono>
#include<thread>
std::mutex _mutex;
class SingleInstance
{

public:
    // 获取单例对象
    static SingleInstance* GetInstance();

    // 释放单例,进程退出时调用
    static void deleteInstance();

    // 打印单例地址
    void Print();

private:
    // 将其构造和析构成为私有的, 禁止外部构造和析构
    SingleInstance();
    ~SingleInstance();

    // 将其拷贝构造和赋值构造成为私有函数, 禁止外部拷贝和赋值
    SingleInstance(const SingleInstance& signal);
    const SingleInstance& operator=(const SingleInstance& signal);

private:

    static SingleInstance* m_SingleInstance;
};

//初始化静态成员变量
SingleInstance* SingleInstance::m_SingleInstance = NULL;

SingleInstance* SingleInstance::GetInstance()
{

    if (m_SingleInstance == NULL)
    {
        m_SingleInstance = new (std::nothrow) SingleInstance;  
    }

    return m_SingleInstance;
}

void SingleInstance::deleteInstance()
{
    if (m_SingleInstance)
    {
        delete m_SingleInstance;
        m_SingleInstance = NULL;
    }
}

void SingleInstance::Print()
{
    std::cout << "我的实例内存地址是:" << this << std::endl;
}

SingleInstance::SingleInstance()
{
    std::cout << "构造" << std::endl;
}

SingleInstance::~SingleInstance()
{
    std::cout << "析构" << std::endl;
}

// 线程函数
void PrintHello(void* threadid)
{
    // 主线程与子线程分离,两者相互不干涉,子线程结束同时子线程的资源自动回收
   
    // 对传入的参数进行强制类型转换,由无类型指针变为整形数指针,然后再读取
    int tid = *((int*)threadid);
    _mutex.lock();
    std::cout << "Hi, 我是线程 ID:[" << tid << "]" << std::endl;
    _mutex.unlock();
    // 打印实例地址
    SingleInstance::GetInstance()->Print();
    

}

#define NUM_THREADS 4// 线程个数

int main(void)
{
   
    
    int indexes[NUM_THREADS] = { 0 }; // 用数组来保存i的值

    int ret = 0;
    int i = 0;
    
    std::cout << "开始 ... " << std::endl;
    for (int i = 0; i < NUM_THREADS; i++) {
        _mutex.lock();
        std::cout << "创建线程:" << i << std::endl;
        _mutex.unlock();
        indexes[i] = i; //先保存i的值
        std::thread t(&PrintHello, (void*)&(indexes[i]));
        t.detach();
    }
    
    std::this_thread::sleep_for(std::chrono::milliseconds(3000));
    // 手动释放单实例的资源
    SingleInstance::deleteInstance();
    std::cout << " 结束! " << std::endl;

    return 0;
}

三、饿汉式单例

class Singleton
{
public:
    // 获取单实例
    static Singleton* GetInstance();

    // 释放单实例delete
    static void deleteInstance();
    
    // 打印
    void Print();

private:
    // 私有的, 禁止外部构造和析构
    Singleton();
    ~Singleton();

    // 私有函数, 禁止外部拷贝和赋值
    Singleton(const Singleton &signal);
    const Singleton &operator=(const Singleton &signal);

private:
    // 实例对象指针
    static Singleton *g_pSingleton;
};

// 线程安全
Singleton* Singleton::g_pSingleton = new (std::nothrow) Singleton;

Singleton* Singleton::GetInstance()
{
    return g_pSingleton;
}

void Singleton::deleteInstance()
{
    if (g_pSingleton)
    {
        delete g_pSingleton;
        g_pSingleton = NULL;
    }
}

void Singleton::Print()
{
    std::cout << "this:" << this << std::endl;
}

Singleton::Singleton()
{
}

Singleton::~Singleton()
{
}

懒汉式用到才会创建,所以线程少的时候合适
饿汉式反过来适合线程比较多,访问多的时候

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值