C++单例模式思考

C++单例模式思考

1. 简单实现

单例顾名思义,就是全局唯一,有且只有一个对象,不允许同时存在多个对象,所以通常单例在C++里面有固有的实现格式——将构造函数声明成private或者protected,目的是不让外部使用者通过构造函数来创建对象。对外暴露一个静态函数获取单例对象,对内维护全局唯一的静态对象实例。具体实现如下所示:

class Singleton {
protected:
    Singleton(){ }

private:
    static void SingletonInit() {
        single_object_ = new Singletone();
    }

public:
    virtual ~Singleton(){ }
    static Singleton* GetInstance() {
        if (NULL == single_object_) {
            pthread_once(&once_, &SingletonInit);
        }

        return single_object_;
    }

private:
    static Singleton* single_object_;
    static pthread_once_t once_;
};

这里用到了pthread_once调用,目的是保证单例的创建在多线程环境下是线程安全的。对外仅仅暴露GetInstance方法来获取单例类的对象。当然这是最简单的单例的实现,那么问题来了,如何将单例的实现逻辑抽象出来作为单例基类?如何保证单例对象在对象周期结束之后能够被正确释放,而不会造成内存泄露?

2.智能指针加模板实现单例基类

为了抽象出单例基类,利用模板,将单例逻辑抽象出来,内部维护的单例对象为T*类型。与此同时,为了让单例维护的内存空间能够被正确释放,这里采用智能指针代替裸指针,具体实现如下代码所示:

#include <pthread.h>
#include <tr1/memory>

template<class T>
class Singleton {
protected:
    Singleton(){ }

private:
    static void SingletonInit() {
        single_object_.reset( new T );
    }

public:
    virtual ~Singleton(){ }
    static std::tr1::shared_ptr<T> GetInstance() {
        if(NULL == single_object_.get()) {
            pthread_once(&once_, &SingletonInit); 
        }

        return single_object_;
    }

private:
    static std::tr1::shared_ptr<T> single_object_;
    static pthread_once_t once_;
};

//using pthread_once() the keep multi thread safe
template<class T>
pthread_once_t Singleton<T>::once_ = PTHREAD_ONCE_INIT;

template<class T>
std::tr1::shared_ptr<T> Singleton<T>::single_object_;

有几点需要注意,由于采用了智能指针,在单例基类中,其析构函数必须声明成public(这是因为智能指针以对象的方式管理资源,在智能指针内部会调用所维护对象的析构函数来释放资源)。那么问题是如何使用单例基类呢?在这里,我结合工厂模式和单例模式,利用单例基类实现一个单例工厂,具体工厂类声明代码如下:
‘ObjectFactory.h’

#ifndef OBJECTFACTORY_
#define OBJECTFACTORY_
#include "Object.h"
#include "Singleton.h"

#include <tr1/memory>
#include <map>

using namespace std;

class Object;
class ObjectFactory:public Singleton<ObjectFactory> {
    friend class Singleton<ObjectFactory>;

private:
    ObjectFactory() { }

public:
    ~ObjectFactory(){ 
    }

public:
    //this is also thread unsafe;
    tr1::shared_ptr<Object> GetObject( string name ) {
        if( factory_map_.count(name) == 0 ) {
            factory_map_[name].reset(new Object(name));
        }

        return factory_map_[name];
    }

private:
    map<string , tr1::shared_ptr<Object> > factory_map_;
};

#endif

在单例工厂中,单例工厂继承单例基类,并且需要声明单例基类为工厂类的友元类,这是因为单例基类会显示调用单例工厂的构造函数来创建对象,但是为了保证单例工厂构造函数不至于被外部显示调用,单例工厂构造函数必须声明为private或者protected,所以这里需要声明友元类,单例基类才可以调用单例工厂类的构造函数来构造对象。

这段代码存在一个问题,在工厂类中生产对象的时候也会存在线程安全问题,所以这里在单例工厂类生产对象的时候进行加锁,避免重复生产对象造成内存的泄露。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值