C++ 单例设计模式

来源:互联网 发布:网络集成版驱动精灵 编辑:程序博客网 时间:2024/06/05 11:23

有些类只需要有一个实例,比如软件开发过程中的日志功能,因为我们要频繁的写log,不可能每次都要重新new一个日志实例,然后用完再delete。当然在C++中可以用全局变量来代替,但全局变量有太多的不可预测性,特别是在多线程的开发中,所以对全局变量我们是能不用就不用。此时单例设计模式就派上用场了。先上代码,在分析为何这么写。

// Singleton.h#pragma once#include <windows.h>class CSingleton{public:    static CSingleton* GetInstance();    bool WriteLog(const TCHAR* szLogPath, const TCHAR* szLogInfo);private:    CSingleton(){}; //    ~CSingleton(){};    CSingleton(const CSingleton&);    CSingleton& operator=(const CSingleton&);    static CSingleton* m_Instance;    static HANDLE m_pSingleMutex;    class CGarbo    {    public:        ~CGarbo()        {            if (m_Instance != NULL)            {                WaitForSingleObject(m_pSingleMutex, INFINITE);                if (m_Instance != NULL)                {                    delete m_Instance;                    m_Instance = NULL;                }                ReleaseMutex(m_pSingleMutex);            }            if (m_pSingleMutex != NULL)            {                CloseHandle(m_pSingleMutex);                m_pSingleMutex = NULL;            }        }    };    static CGarbo m_Garbo;};

单例模式的定义: 保证一个类只有一个实例,并提供一个可供全局访问的访问点。

  1. m_Instance是类的唯一实例,把它申明成静态私有变量,通过公有函数GetInstance返回
  2. 类要创建实例需调用类的构造函数,为了防止类外部创建类的实例,把类的构造函数申明私有
  3. CGarbo 用来进行资源释放
    程序运行结束时,系统会自动调用静态成员m_Garbo的析构函数,该析构函数进行资源释放,这种资源的释放是在程序员不知道的情况下进行的;
    —— 程序运行结束时,系统会自动析构所有的全局变量,我们知道全局变量和静态变量都储存在静态存储区,所以在析构时,也会析构静态成员变量
// Singleton.cpp#include "stdafx.h"#include "Singleton.h"#include <fstream>using namespace std;CSingleton* CSingleton::m_Instance = NULL;CSingleton::CGarbo CSingleton::m_Garbo;HANDLE CSingleton::m_pSingleMutex = CreateMutex(NULL, FALSE, NULL);CSingleton* CSingleton::GetInstance(){    if (m_Instance == NULL)    {        WaitForSingleObject(m_pSingleMutex, INFINITE);        if (m_Instance == NULL)        {            m_Instance = new CSingleton();        }        ReleaseMutex(m_pSingleMutex);    }    return m_Instance;}bool CSingleton::WriteLog(const TCHAR* szLogPath, const TCHAR* szLogInfo){    FILE * pLogFile;    int nRtn = -1;    _tfopen_s(&pLogFile, szLogPath, _T("a"));    if (pLogFile == NULL)    {        return false;    }    nRtn = _ftprintf(pLogFile, _T("%s\n"), szLogInfo);    if (nRtn < 0)    {        fflush(pLogFile);        fclose(pLogFile);        return false;    }    fflush(pLogFile);    fclose(pLogFile);    return true;}

GetInstance()代码分析:

// 普通单例实例化,线程不安全// 如果多个线程同时调用GetInstance(),假设其中A线程执行完1后且此时m_Instance=NULL,该线程挂起,// 然后当B线程调用GetInstance()时,m_Instance=NULL仍为空,B线程会创建CSingleton的一个实例// 某个时刻当A线程继续执行时,由于之前1语句已经执行完,故A线程也会创建CSingleton的一个实例,这和单例模式的初衷是不符合的CSingleton* CSingleton::GetInstance(){    if (m_Instance == NULL)/*1*/    {        m_Instance = new CSingleton();/*2*/    }    return m_Instance;}// 为了线程安全问题,在创建实例的时候加锁,创建完后释放// 当有多个线程调用GetInstance()时,每次都要加锁解锁,频繁的锁操作对性能是有影响的// 并且当多个线程同时调用GetInstance()时,只有一个线程能够继续执行,其他线程要先等待,同样对性能也有影响CSingleton* CSingleton::GetInstance(){    WaitForSingleObject(m_pSingleMutex, INFINITE);/*Lock*/    if (m_Instance == NULL)    {        m_Instance = new CSingleton();    }    ReleaseMutex(m_pSingleMutex);/*UnLock*/    return m_Instance;}// 为了解决上述问题,引入了“双锁检”机制(1 And 3)// 当有多个线程调用GetInstance()时,如果m_Instance为NULL,GetInstance()直接返回,既不用频繁的锁操作,线程也不用等待// 那为什么要有3了?  原因和普通单例线程不安全的原因相同CSingleton* CSingleton::GetInstance(){    if (m_Instance == NULL)/*1*/    {        WaitForSingleObject(m_pSingleMutex, INFINITE);/*2*/        if (m_Instance == NULL)/*3*/        {            m_Instance = new CSingleton();        }        ReleaseMutex(m_pSingleMutex);    }    return m_Instance;}

Last

// main 函数中调用CSingleton *singleObj = CSingleton::GetInstance();singleObj->WriteLog(_T("D:\\Single.log"), _T("info000000"));