C++之单例模式

C++之单例模式

单例模式也称为单件模式、单子模式,可能是使用最广泛的设计模式。其意图是保证一个类仅有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享。有很多地方需要这样的功能模块,如系统的日志输出,GUI应用必须是单鼠标,MODEM的联接需要一条且只需要一条电话线,操作系统只能有一个窗口管理器,一台PC连一个键盘。

方法一:饿汉模式,即指单例实例在程序运行时被立即执行初始化

class Singleton  {
public:
    static Singleton * Instance() {
        return &m_pInstance;
    }
private:
    Singleton ();              // ctor is hidden
    Singleton (Singleton  const&);    // copy ctor is hidden
    static Singleton  m_pInstance;
};
// in Singleton.cpp we have to add
Singleton  Singleton ::m_pInstance;

此种方法不足:如果有两个单例模式的类 ASingleton 和 BSingleton, 某天你想在 BSingleton 的构造函数中使用 ASingleton 实例, 这就出问题了. 因为 BSingleton m_pInstance 静态对象可能先 ASingleton 一步调用初始化构造函数, 结果 ASingleton::Instance() 返回的就是一个未初始化的内存区域, 程序还没跑就直接崩掉.

方法二:懒汉模式,即单例实例只在第一次被使用时进行初始化

class Singleton {
public:
    static Singleton* Instance() {
        if (!m_pInstance)
            m_pInstance = new Singleton;
        return m_pInstance;
    }
private:
    Singleton();        // ctor is hidden
    Singleton(Singleton const&);    // copy ctor is hidden
    static Singleton* m_pInstance;
};
// in .cpp we have to add
Singleton* Singleton::m_pInstance = NULL;

Instance() 只在第一次被调用时为 m_pInstance 分配内存并初始化. 嗯, 看上去所有的问题都解决了, 初始化顺序有保证, 多态也没问题.

不过细心的你可能已经发现了一个问题, 程序退出时, 析构函数没被执行. 这在某些设计不可靠的系统上会导致资源泄漏, 比如文件句柄, socket 连接, 内存等等. 幸好 Linux / Windows 2000/XP 等常用系统都能在程序退出时自动释放占用的系统资源. 不过这仍然可能是个隐患,有些系统是不会自动释放的.

对于这个问题, 比较土的解决方法是, 给每个 Singleton 类添加一个 destructor() 方法,采用RAII方法:

class CSingleton:
{
  public:  
    static CSingleton * GetInstance()  
private:  
    CSingleton(){};  
    static CSingleton * m_pInstance;  
    class CGarbo // 它的唯一工作就是在析构函数中删除CSingleton的实例  
    {
    public:
        ~CGarbo()
        {  
            if (CSingleton::m_pInstance)
                delete CSingleton::m_pInstance;
       CSingleton::m_pInstance=-=NULL;
        }
    };
    static CGarbo Garbo; // 定义一个静态成员,在程序结束时,系统会调用它的析构函数
};

然后在程序退出时确保调用了每个 Singleton 类的 destructor() 方法, 这么做虽然可靠, 但却很是繁琐。

考虑到线程安全、异常安全,方法二可以做以下扩展:

class Lock  {  
private:         
    CCriticalSection m_cs;  
public:  
    Lock(CCriticalSection  cs) : m_cs(cs)   {  
        m_cs.Lock();  
    }  
    ~Lock()    {  
        m_cs.Unlock();  
    }  
};  //RAII模式
class Singleton  {  
private:  
    Singleton();  
    Singleton(const Singleton &);  
    Singleton& operator = (const Singleton &);  
    class CGarbo   {
       public:
          ~CGarbo()        {  
            if (CSingleton::m_pInstance){
                 delete CSingleton::m_pInstance;
                 CSingleton::m_pInstance=-=NULL;
            }
     };
    static CGarbo Garbo; // 定义一个静态成员,在程序结束时,系统会调用它的析构函数 

public:  
    static Singleton *Instantialize();  
    static Singleton *pInstance;  
    static CCriticalSection cs;  
};  

Singleton* Singleton::pInstance = 0;
Singleton* Singleton::Instantialize()  
{  
    if(pInstance == NULL)  
    {   //double check  
        Lock lock(cs);           
        //用lock实现线程安全,用资源管理类,实现异常安全  
        //使用资源管理类,在抛出异常的时候,资源管理类对象会被析构,析构总    是发生的无论是因为异常抛出还是语句块结束。  
        if(pInstance == NULL)        
            pInstance = new Singleton();          
    }  
    return pInstance;  
}


方法三、最优实现写法

class Singleton {
public:
    static Singleton& Instance() {
        static Singleton theSingleton;
        return theSingleton;
    }   
private:
    Singleton();                            // ctor hidden
    Singleton(Singleton const&);            // copy ctor hidden
    Singleton& operator=(Singleton const&); // assign op. hidden
    ~Singleton();                           // dtor hidden
};

在 Instance() 函数内定义局部静态变量的好处是, theSingleton的构造函数只会在第一次调用 Instance() 时被初始化, 达到了和 “堆栈版” 相同的动态初始化效果, 保证了成员变量和 Singleton 本身的初始化顺序.

它还有一个潜在的安全措施, Instance() 返回的是对局部静态变量的引用, 如果返回的是指针, Instance() 的调用者很可能会误认为他要检查指针的有效性, 并负责销毁. 构造函数和拷贝构造函数也私有化了, 这样类的使用者不能自行实例化.

另外, 多个不同的 Singleton 实例的析构顺序与构造顺序相反.

同样可以用Lock保证线程安全。

此处的单例模式特点如下:
1. 它有一个指唯一实例的静态指针m_pInstance,并且是私有的。
2. 它有一个公有的函数,可以获取这个唯一的实例,并在需要的时候创建该实例。
3. 它的构造函数是私有的,这样就不能从别处创建该类的实例。

参考链接:

  1. http://stackoverflow.com/questions/86582/singleton-how-should-it-be-used

  2. http://blog.youkuaiyun.com/taiyang1987912/article/details/43202271

  3. http://www.cnblogs.com/wxxweb/archive/2011/04/15/2017088.html

  4. http://www.programlife.net/cpp-singleton-memory-retrieve.html

  5. http://stackoverflow.com/questions/270947/can-any-one-provide-me-a-sample-of-singleton-in-c/271104#271104

  6. http://stackoverflow.com/questions/1008019/c-singleton-design-pattern/1008289#1008289

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值