实现Singleton模式

题目:设计一个类,我们只能生产该类的一个实例。

C#版本

  • 思路一:只适用于单线程环境
public sealed class Singleton1{
    private Singleton1(){}//构造函数设为私有函数以禁止他人创建实例

    private static Singleton1 instance = null;//静态实例
    public static Singleton1 Instance{
        get{
            if (instance == null){
                instance = new Singleton1();//在需要的时候创建该实例
            }
            return instance;
        }
    }
}

上述代码存在的问题:两个线程同时运行到判断instance是否为null的if语句,并且instance的确没有创建时,那么两个线程都会创建一个实例,此时类型Singleton1就不再满足单例模式的要求。

  • 思路二:可在多线程环境中工作,但效率不高。
public sealed class Singleton2{
    private Singleton2(){}
    private static readonly object syncObj = new object();

    private static Singleton2 instance = null;
    public static Singleton2 Instance{
        get{
            lock(syncObj){//同步锁
                if (instance == null){
                    instance = new Singleton2();
                }
            }
            return instance;
        }
    }
}

上述代码缺点:每次通过属性Instance得到Singleton2的实例都会试图加上一个同步锁,而加锁是一个非常耗时的操作。

  • 思路三:加同步锁前后两次判断实例是否已存在
public sealed class Singleton2{
    private Singleton2(){}

    private static object syncObj = new object();

    private static Singleton3 instance = null;
    public static Singleton3 Instance{
        get{
            if (instance == null){//只在instance没有创建时加锁
                lock(syncObj){
                    if (instance == null){
                        instance = new Singleton3();
                    }
                }
            }
            return instance;
        }
    } 
}

上述代码缺点:用两个if,代码复杂。

  • 强烈推荐解法一:利用静态构造函数
public sealed class Singleton4{
    private Singleton4(){}
    /*
    在初始化静态变量instance的时候创建实例。由于C#在调用静态构造函数时初始化静态变量,.NET运行时能够确保只调用一次静态构造函数,这样就能够保证只初始化一次instance*/
    private static Singleton4 instance = new Singleton4();
    public static Singleton4 Instance{
        get{
            return instance;
        }
    }
}

上述代码的缺点:C#中调用静态构造函数的时机不是由程序员掌控的,而是当.NET运行时第一次使用一个类型的时候自动调用该类型的静态构造函数。因此在Singleton4中,实例instance并不是第一次调用属性Singleton.Instance的时候创建,而是在第一次用到Singleton4的时候就会被创建。假设我们在Singleton4中添加一个静态方法,调用该静态函数时不需要创建一个实例的,但如果按照Singleton4的方法实现单例模式,则仍然会过早地创建实例,从而降低内存的使用效率。

  • 强烈推荐解法二:实现按需创建实例
public sealed class Singleton5{
    Singleton5(){}

    public static Singleton5 Instance{
        get{
            return Nested.instance;
        }
    }

    class Nested{//内部私有类型
        static Nested(){}
        internal static readonly Singleton5 instance = new Singleton5();
    }
}

当第一次用到嵌套类型Nested的时候,会调用静态构造函数创建Singleton5 的实例instance。类型Nested只在属性Singleton5.Instance中被用到,由于其私有属性他人无法使用Nested类型。因此当我们第一次试图通过属性Singleton5.Instance得到Singleton5的实例时,会自动调用Nested的静态构造函数创建实例instance。如果我们不调用属性Singleton5.Instance,那么久不会触发.NET运行时调用Nested,也不会创建实例,这样就真正做到了按需创建。

C++版

  • 第一版:保证只产生一个对象。对GetInstance稍加修改,这个设计模板便可以适用于可变多实例情况,如一个类允许最多五个实例。
class CSingleton
{
private:
    CSingleton()   //构造函数是私有的
    {
    }
    static CSingleton *m_pInstance;
public:
    static CSingleton * GetInstance()
    {
        if(m_pInstance == NULL)  //判断是否第一次调用
            m_pInstance = new CSingleton();
        return m_pInstance;
    }
};
  • 第二版:解决参数传递问题和赋值问题。
class CSingleton
{
private:
    CSingleton()   //构造函数是私有的
    {
    }
    CSingleton(const Singleton& s){}
    CSingleton& operator=(const CSingleton& s){}
    static CSingleton *m_pInstance;
public:
    static CSingleton * GetInstance()
    {
        if(m_pInstance == NULL)  //判断是否第一次调用
            m_pInstance = new CSingleton();
        return m_pInstance;
    }
};
  • 第三版:解决内存释放
    • 方案1:程序在结束的时候,系统会自动析构所有的全局变量。事实上,系统也会析构所有的类的静态成员变量,就像这些静态成员也是全局变量一样。利用这个特征,我们可以在单例类中定义一个这样的静态成员变量,而它的唯一工作就是在析构函数中删除单例类的实例。如下面的代码中的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;
    }
};
  • 方案2
class CSingleton
{
private:
    CSingleton()   //构造函数是私有的
    {
    }
    CSingleton(const CSingleton &);
    CSingleton & operator = (const CSingleton &);
public:
    static CSingleton & GetInstance()
    {
        static CSingleton instance;   //局部静态变量
        return instance;
    }
};
  • 解决线程安全问题
class Lock
{
private:       
    CCriticalSection m_cs;
public:
    Lock(CCriticalSection  cs) : m_cs(cs)
    {
        m_cs.Lock();
    }
    ~Lock()
    {
        m_cs.Unlock();
    }
};

class Singleton
{
private:
    Singleton();
    Singleton(const Singleton &);
    Singleton& operator = (const Singleton &);

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;
}

扩展问题

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
单例模式Singleton Pattern)是一种创建型模式,它保证一个类只有一个实例,并提供一个全局访问点。单例模式通常用于控制某些资源的访问权限,或者在整个系统中只需要一个共享资源的情况下使用。 在单例模式中,类的构造函数必须私有化,这样外部就不能直接实例化该类。同时,类中需要定义一个静态方法,用于获取该类唯一的实例。在第一次调用该方法时,会创建一个实例,并将其保存下来。以后每次调用该方法时,都会返回同一个实例。 例如,下面的代码演示了如何在C++中实现单例模式: ```c++ class Singleton { public: static Singleton& getInstance() { static Singleton instance; // 延迟初始化,保证线程安全 return instance; } private: Singleton() {} // 将构造函数私有化,防止外部实例化 Singleton(const Singleton&) = delete; // 禁止拷贝构造函数 Singleton& operator=(const Singleton&) = delete; // 禁止赋值运算符 }; int main() { Singleton& s1 = Singleton::getInstance(); Singleton& s2 = Singleton::getInstance(); std::cout << std::boolalpha << (&s1 == &s2) << std::endl; // 输出:true return 0; } ``` 在这个例子中,我们定义了一个名为Singleton的类,并将其构造函数私有化,防止外部实例化。同时,我们定义了一个静态方法getInstance,用于获取该类唯一的实例。在getInstance方法中,我们使用了静态局部变量的方式来延迟初始化,保证线程安全。最后,在main函数中,我们多次调用getInstance方法,每次调用都会返回同一个实例。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值