您的位置:首页 > 编程语言 > C语言/C++

C++单例singleton模式_legend

2014-05-05 00:09 239 查看

     单例模式 Singleton

一: 简介

 (一个类只有一个实例对象,用一个全局指针取访问它)

 单例模式的意图就是保证一个类仅有一个实例对象,该实例对象被所有的程序模块所共享。不同于全局对象,因为全局对象虽然可以被所以模块共享,但是不能保证只有一个对象,即可以创建一个局部对象来覆盖全局对象。

 应用:如系统日志的输出,GUI应用必须是单鼠标,操作系统只能有一个窗口管理器,一台PC连着一个键盘。

二: 思想

 单例模式通过类本身来管理其唯一实例,使用类的私有静态指针变量指向类的唯一实例,并用一个公有的静态方法获取该实例。

三: 应用

  (1)优点:

 1.单例模式在内存中只有一个实例,减少内存开支。

 适用于对象被频繁的创建,销毁。

 2.避免对资源的多重占用。

 如:一个写文件动作,由于只有一个实例在内存中,

 避免对同一个资源文件的同时写操作。

 3.用单例模式设置全局访问点。

  (2)缺点:

   1.单例模式没有接口,难以扩展。

   因为单例模式,要求自行实例化,并且提供单一实例,

   接口或者抽象类是无法被实例化的。

  (3)应用场景:

  在一个系统中,要求一个类只有一个对象、具体场景如下:

  (1.对象被频繁的创建,销毁 2.对象的创建需要消耗大量资源 3.共享数据)

   .要求生成唯一序列号的环境。

   .整个项目中需要一个共享数据。如Web 页面上的计数器,

   不需要每次刷新时都记录在数据库中,可以使用单例模式保持

   计数器的值,并确保是线程安全的 。

   .创建一个对象需要消耗的资源过多,如访问IO,访问数据库等资源。

   .需要定义大量的静态常量或者静态方法的环境,可以采用单例模式

   或者使用static .

四: 范例

 (1)范例一

 class CSingleton

{

private:

 CSingleton()   //构造函数是私有的

 {

 }

 static CSingleton *m_pInstance;

public:

 static CSingleton * GetInstance()

 {

  if(m_pInstance == NULL)  //判断是否第一次调用

   m_pInstance = new CSingleton();

  return m_pInstance;

 }

};

 解析一:

 1.用户访问唯一实例的方法只有GetInstance()成员函数:

 如果不通过这个函数,任何创建实例的尝试都将失败,因为类的构造函数是私有的。

 2.所有GetInstance()之后的调用都返回相同实例的指针:

 GetInstance()使用懒惰初始化,也就是说它的返回值是当这个函数首次被访问时被创建的。因为创建了之后m_pinstance就不在为null ,

 不会new 一个新的实例了。

 如:CSingleton* p1 = CSingleton :: GetInstance();

 CSingleton* p2 = p1->GetInstance();

 CSingleton & ref = * CSingleton :: GetInstance();

 (p1,p2的值是相同的。)

 3.扩展:

 单例模式singleton是一个类只有一个实例对象,可以稍加修改getInstance函数,该设计模板就可以适用于可变多实例情况,如一个类最多有五个实例。(五个私有static 静态成员对象指针,getInstacnce中每一个的操作与之类似。)

 4.特征:

   1)一个类只有一个实例对象

   2)私有static 成员对象指针m_instance;

   3) 私有构造函数

   4)公有的成员函数getInstance获取实例,返回m_instance;

   注意instance 中 的写法。

 5.问题:

 m_instance指向的对象什么时候释放?析构函数何时执行?

 6.释放该实例。

 可以在程序结束时调用GetInstance(),并对返回的指针掉用delete操作。这样做可以实现功能,但不仅很丑陋,而且容易出错。因为这样的附加代码很容易被忘记,而且也很难保证在delete之后,没有代码再调用GetInstance函数。

 一个妥善的方法是让这个类自己知道在合适的时候把自己删除,或者说把删除自己的操作挂在操作系统中的某个合适的点上,使其在恰当的时候被自动执行。

 7、线程不安全:

 解析:该单例模式在较低的并发情况下尚不会出现问题,若系统压力增大,并发量增加时则可能在内存中出现多个实例,破坏了最初的预期。如:一个线程A执行到singleton = new Singleton(),但还没有获得对象(对象初始化是需要时间的),第二个线程B也在执行,执行到(singleton == null)判断,那么B线程获得判断条件也是为真,于是继续运行下去,A线程获得了一个对象,B线程也获得了一个对象,在内存中就出现两个对象!

 8.线程不安全改进1:

 public class Singleton {

 

 private static final Singleton singleton = new Singleton();

 

 //限制产生多个对象

 

 private Singleton(){

 

 }

 

 //通过该方法获得实例对象

 

 public static Singleton getSingleton(){

 

 return singleton;

 

 }

 

 //类中其他方法,尽量是static

 

 public static void doSomething(){

 

 }

 

 }

 9,线程不安全改进2:

 注意:线程安全,采用互斥体的方式实现。

看代码:

//Emperor.h

#pragma once

#include <iostream>

using std::cout;

using std::endl;

using std::string;

class CEmperor

{

public:

    static CEmperor * GetInstance();

    static void ReleaseInstance();

    void EmperorInfo(void);

    void SetEmperorTag(string tag);

private:

    CEmperor(void);

    virtual ~CEmperor(void);

    CEmperor(const CEmperor&);

    CEmperor& operator=(const CEmperor&);

    static CEmperor *m_pEmperor;

    static HANDLE m_pMutex;

    string m_EmperorTag;

    class CGarbo

    {

    public:

        CGarbo()

        {

            cout << "Create Garbo" << endl;

        }

        ~CGarbo()

        {

            cout << "Destroy Garbo" << endl;

            if (NULL != m_pEmperor)

            {

                WaitForSingleObject(m_pMutex, INFINITE);

                if (NULL != m_pEmperor)

                {

                    cout << "Remove instance" << endl;

                    delete m_pEmperor;

                    m_pEmperor = NULL;

                }

                ReleaseMutex(m_pMutex);

            }

            if (NULL != m_pMutex)

            {

                cout << "Delete mutex" << endl;

                CloseHandle(m_pMutex);

                m_pMutex = NULL;

            }

        }

    };

    static CGarbo m_Garbo;

};

//Emperor.cpp

#include "StdAfx.h"

#include "Emperor.h"

#include <iostream>

using std::cout;

using std::endl;

using std::string;

CEmperor* CEmperor::m_pEmperor = NULL;

HANDLE CEmperor::m_pMutex = CreateMutex(NULL, FALSE, NULL);

CEmperor::CGarbo CEmperor::m_Garbo;

CEmperor::CEmperor(void)

{

    cout << "Create CEmperor Instance" << endl;

}

CEmperor::~CEmperor(void)

{

    cout << "Destroy CEmperor Instance and release its resource" << endl;

}

void CEmperor::EmperorInfo(void)

{

    char msgBuffer[50] = { 0 };

    sprintf_s(msgBuffer, 50, "皇ê帝?某3某3某3... ...(%s).", m_EmperorTag.c_str());

    string msg(msgBuffer);

    cout << msg.c_str() << endl;

}

CEmperor* CEmperor::GetInstance()

{

    if (NULL == m_pEmperor)

    {

        WaitForSingleObject(m_pMutex, INFINITE);

        if (NULL == m_pEmperor)

            m_pEmperor = new CEmperor();

        ReleaseMutex(m_pMutex);

    }

    return m_pEmperor;

}

void CEmperor::ReleaseInstance()

{

    if (NULL != m_pEmperor)

    {

        WaitForSingleObject(m_pMutex, INFINITE);

        if (NULL != m_pEmperor)

        {

            delete m_pEmperor;

            m_pEmperor = NULL;

        }

        ReleaseMutex(m_pMutex);

    }

}

void CEmperor::SetEmperorTag( string tag )

{

    m_EmperorTag = tag;

}

//Singleton.cpp

#include "stdafx.h"

#include "Emperor.h"

void DoIt()

{

    CEmperor *pEmperor1 = CEmperor::GetInstance();

    pEmperor1->SetEmperorTag("95");

    pEmperor1->EmperorInfo();

    CEmperor *pEmperor2 = CEmperor::GetInstance();

    pEmperor2->EmperorInfo();

    CEmperor *pEmperor3 = CEmperor::GetInstance();

    pEmperor3->EmperorInfo();

    CEmperor *pEmperor4 = pEmperor3;

    pEmperor4->EmperorInfo();

    CEmperor *pEmperor5 = NULL;

    pEmperor5 = pEmperor4;

    pEmperor5->EmperorInfo();

    CEmperor::ReleaseInstance();

}

int _tmain(int argc, _TCHAR* argv[])

{

    DoIt();

    _CrtSetDbgFlag(_CRTDBG_LEAK_CHECK_DF | _CRTDBG_ALLOC_MEM_DF);

    _CrtDumpMemoryLeaks();

    return 0;

}

 范例二:

 程序在结束的时候,系统会自动析构所有的全局变量。事实上,系统也会析构所有的类的静态成员变量,就像这些静态成员也是全局变量一样。利用这个特征,我们可以在单例类中定义一个这样的静态成员变量,而它的唯一工作就是在析构函数中删除单例类的实例。如下面的代码中的

 CGarbo类(Garbo意为垃圾工人):

class CSingleton

{

private:

 CSingleton()

 {

 }

 static CSingleton *m_pInstance;

 class CGarbo  

 //它的唯一工作就是在析构函数中删除CSingleton的实例

 {

 public:

  ~CGarbo()

  {

   if(CSingleton::m_pInstance)

    delete CSingleton::m_pInstance;

  }

 };

 static CGarbo Garbo; 

 //定义一个静态成员变量,程序结束时,系统会自动调用它的析构函数

public:

 static CSingleton * GetInstance()

 {

  if(m_pInstance == NULL)  //判断是否第一次调用

   m_pInstance = new CSingleton();

  return m_pInstance;

 }

};

解析二:

1.

类CGarbo被定义为CSingleton的私有内嵌类,以防该类被在其他地方滥用2.

程序运行结束时,系统会调用CSingleton的静态成员Garbo的析构函数,该析构函数会删除单例的唯一实例。

3.特征:

 1)一个类只有一个实例

 2)私有static 成员对象指针pInstance(初始化为null),私有构造函数。

 3)公有static成员函数getInstance 获取实例指针(注意getInstance 写法)。

 4)定义私有嵌套类,在嵌套类的析构函数中释放该单例对象。

 5)私有的静态成员嵌套类对象。

范例三:

class CSingleton

{

private:

 CSingleton()   //构造函数是私有的

 {

 }

public:

 static CSingleton & GetInstance()

 {

  static CSingleton instance;   //局部静态变量

  return instance;

 }

};

问题:

Singleton singleton = Singleton :: GetInstance();

这么做就出现了一个类拷贝的问题,这就违背了单例的特性。

改进:

禁止类拷贝和类赋值,禁止程序员用这种方式来使用单例、

GetInstance()函数返回一个指针而不是返回一个引用,函数的代码改为如下:

class CSingleton

{

private:

 CSingleton()   //构造函数是私有的

 {

 }

public:

 static CSingleton * GetInstance()

 {

  static CSingleton instance;   //局部静态变量

  return &instance;

 }

};

范例四:完整范例。

//Singleton.h

#ifndef _SINGLETON_H_

#define _SINGLETON_H_

#include <iostream>

using namespace std;

class Singleton

{

public:

static Singleton* Instance();

protected:

Singleton();

private:

static Singleton* _instance;

};

#endif //~_SINGLETON_H_

//Singleton.cpp

#include "Singleton.h"

#include <iostream>

using namespace std;

Singleton* Singleton::_instance = 0;

Singleton::Singleton()

{

cout<<"Singleton...."<<endl;

}

Singleton* Singleton::Instance()

{

if (_instance == 0)

{

_instance = new Singleton();

}

return _instance;

}

//main.cpp

#include "Singleton.h"

#include <iostream>

using namespace std;

int main(int argc,char* argv[])

{

Singleton* sgn = Singleton::Instance();

return 0;

}

五: 要点总结。

 1)要点:

   1.私有static 成员对象指针pInstance,私有构造函数

   2.私有static 属性,来给该单例实例设置属性。

   3.共有static 成员函数getInstance获取实例。

   共有static 成员函数doSomething;

   4.定义内嵌类,以及私有static 内嵌类对象,

   用内嵌类的析构函数来释放该单例实例。

六:单例模式扩展:有上限的多例模式。

(需要产生固定数量对象的模式就叫做有上限的多例模式,它是单例模式的一种扩展,采用有上限的多例模式,我们可以在设计时决定在内存中有多少个实例,方便系统进行扩展)

如:如读取文件,我们可以在系统启动时完成初始化工作,在内存中启动固定数量的reader实例,然后在需要读取文件时就可以快速响应。

如果一个类可以产生多个对象,对象的数量不受限制,直接使用new关键字就可以了,如果只要有一个对象,使用单例模式就可以了,但是如果要求一个类只能产生两个、三个对象呢?

范例:

public class Emperor {

 

//定义最多能产生的实例数量

 

private static int maxNumOfEmperor = 2;

 

//每个皇帝都有名字,使用一个ArrayList来容纳,每个对象的私有属性

 

private static ArrayList<String> nameList=new ArrayList<String>();

 

//定义一个列表,容纳所有的皇帝实例

 

private static ArrayList<Emperor> emperorList=new ArrayList<Emperor>();

 

//当前皇帝序列号

 

private static int countNumOfEmperor =0;

 

//产生所有的对象

 

static{

 

for(int i=0;i<maxNumOfEmperor;i++){

 

emperorList.add(new Emperor("皇"+(i+1)+"帝"));

 

}

 

}

 

private Emperor(){

 

//世俗和道德约束你,目的就是不产生第二个皇帝

 

}

 

//传入皇帝名称,建立一个皇帝对象

 

private Emperor(String name){

 

nameList.add(name);

 

}

 

//随机获得一个皇帝对象

 

public static Emperor getInstance(){

 

Random random = new Random();

 

countNumOfEmperor = random.nextInt(maxNumOfEmperor); //随机拉出一个皇帝,只要是个精神领袖就成

 

return emperorList.get(countNumOfEmperor);

 

}

 

//皇帝发话了

 

public static void say(){

 

System.out.println(nameList.get(countNumOfEmperor));

 

}

 

}

public class Minister {

 

public static void main(String[] args) {

 

//定义5个大臣

 

int ministerNum =5;

 

for(int i=0;i<ministerNum;i++){

 

Emperor emperor = Emperor.getInstance();

 

System.out.print("第"+(i+1)+"个大臣参拜的是:");

 

emperor.say();

 

}

 

}

 

}

结果:  

大臣参拜皇帝的结果如下所示。

第1个大臣参拜的是:皇1帝

第2个大臣参拜的是:皇2帝

第3个大臣参拜的是:皇1帝

第4个大臣参拜的是:皇1帝

第5个大臣参拜的是:皇2帝

要点:

1)私有static 成员对象指针数组pInstances.

私有构造函数。

2) 私有static 属性数组,为每一个实例设置属性。

如:此中nameList.

因为 name 作为 Emperor类的一个属性,在单例模式中,仅仅是name

,但是在扩展单例模式中,需要有多个实例,所以要有多个属性,

即属性数组。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  单例singleton模式