【设计模式】C++对单例模式实现的总结(C++11超简化版!!!必看精髓)

单例模式清晰的视频解说https://www.bilibili.com/video/BV1Gz4y1d7RJ?from=search&seid=352768139051489960

问题的提出:

一个国家只能有一个主席,一个学校只能有一个校长…

单例模式:(实现原理)

原理:类的构造器被私有化,并且在类里面只实例化了一个对象,如果你需要用到该类对象,你只能通过getInstance()方法获得类自己生成的这个对象。


它属于创建型模式,它提供了一种创建对象的最佳方式。一个类有且只有一个由自己生成的对象,并且还有一个对外的可供访问该对象的一种方式。(通俗的讲,就是这个类的构造器被私有化了,并且在类里面只实例化了一个对象,如果你需要用到该类对象,你只能通过getInstance()方法获得类自己生成的这个对象)

那么什么时候你需要用到单例模式呢?


比如说:

现在的操作系统大多是多线程的,在操作一个文件的时候,就不可避免地出现多个进程或线程同时操作一个文件的现象,所以所有文件的处理必须通过唯一的实例来进行。例如:日志类(如果不用单例模式,对文件的操作可能会无效,反正会发生很多不可言状的意外)

面对单例模式适用的情况,可能会优先考虑使用[全局变量]或者[静态变量]的方式(全局唯一),这样比较简单,也是没学过设计模式的人所能想到的最简单的方式了。如果采用全局或者静态变量的方式,会影响封装性,难以保证别的代码不会对全局变量造成影响。

考虑到这些需要,我们将默认的构造函数声明为私有的,这样就不会被外部所new了,甚至可以将析构函数也声明为私有的,这样就只有自己能够删除自己了。

在Java和C#这样纯的面向对象的语言中,单例模式非常好实现:

直接就可以(1)在静态区初始化instance,然后(2)通过getInstance返回,这种就被称为饿汉式单例类

也有些写法是(1)在getInstance中new instance然后返回,这种就被称为懒汉式单例类,但这涉及到第一次getInstance的一个判断问题。

扫描二维码关注公众号,回复: 11518279 查看本文章

“可以在静态区初始化instance,然后通过getInstance返回,这种就被称为饿汉式单例类。也有些写法是在getInstance中new instance然后返回,这种就被称为懒汉式单例类,但这涉及到第一次getInstance的一个判断问题。”

缺点
1.懒汉式是以时间换空间的方式。

2.饿汉式是以空间换时间的方式。

1、饿汉模式(一开始就初始化单例对象)


优点:不用担心多线程问题。

缺点:可能在整个程序中就没有用到这个单例对象,造成浪费。(懒汉模式用到了才去实例化)

实现:

class Singleton
{
public:
    static Singleton *GetInstance() //公有static方法,用于获取单例句柄
    {
        return &singleton_;
    }
private:
    Singleton(); //构造函数私有
    static Singleton singleton_ ; //私有static单例对象,类的static 变量,可以不用实例化就调用 
};

Singleton Singleton::singleton_; //statci成员变量在类中只是声明那么就需要在类外部重新定义或者初始化,实际上是给静态成员变量分配内存。

int main()
{
    auto p1 = Singleton::GetInstance();  //类的static 方法,可以不用实例化就直接调用 
    auto p2 = Singleton::GetInstance();
    bool result = ( p1 == p2); //判断是否指向同一个单例对象
    std::cout <<  result << std::endl;
    return 0;
}


2、懒汉模式(需要的时候再实例化单例对象)


优点:不会像饿汉模式一样造成资源浪费。只是需要考虑多线程安全,实现上稍稍复杂一点。

class Singleton
{
public:
    static Singleton* GetInstance()
    {
        if (p_singleton_ == nullptr)//第一次检查:实例化单例对象后,就不会再进入加锁逻辑
        {
            std::lock_guard<std::mutex> lock(mux_);
            if (p_singleton_ == nullptr)//第二次检查:可能两个线程同时通过第一次检查,一个线程获得锁时,可能另外一个线程已经实例化单体
            {
                p_singleton_ = new Singleton();
            }
        }
        return p_singleton_;
    }
private:
    Singleton();
    static Singleton * p_singleton_ ;
    static std::mutex mux_;
};
 
std::mutex Singleton::mux_;//statci成员变量在类中只是声明那么就需要在类外部重新定义或者初始化
Singleton * Singleton::p_singleton_ = nullptr;//statci成员变量在类中只是声明那么就需要在类外部重新定义或者初始化
int main()
{
    auto p1 = Singleton::GetInstance();//类的static 方法,可以不用实例化就调用 
    auto p2 = Singleton::GetInstance();
    bool result=( p1 == p2);
    std::cout <<  result << std::endl;
 
    return 0;
}


其实记住懒汉模式就确保万无一失了,因为当在程序一开始时就调用懒汉模式的GetInstance,就实例化出单例,这等价于饿汉模式。

3、C++11懒汉模式简化版(必看精髓)


但是看起来懒汉模式比较复杂,我们还可以利用C++11对static的改进性质简化代码。

class Singleton
{
public:
    static Singleton* GetInstance()
    {
        static Singleton  singleton;//此变量存在静态区,C++11自带两段检查锁机制来确保static变量实例化一次
        return &singleton;
    }
private:
    Singleton();
};
 
int main()
{
    auto p1 = Singleton::GetInstance();
    auto p2 = Singleton::GetInstance();
    bool result=( p1 == p2);
    std::cout <<  result << std::endl;
 
    return 0;
}


这种单例模式的写法可谓是简单地不能再简单了。为什么正确呢?

其实static变量本身全局就只有一份,与单例对象的性质极其相似。而C++11为了确保只初始化static变量一次,提供了两段检查锁机制(在上述代码的汇编代码中,可以清楚地看到两段检查锁的逻辑)。换言之,C++11对于static变量,自带使用了单例模式,自然不用我们再费事。

4、单例模板


在上述代码的基础上改成模板的形式:

#include <iostream>
#include <mutex>
using namespace std;

class A
{

};

class B
{

};

template <typename T>
class Singleton2
{
public :
    static T *GetInstance()
    {
        static T t;
        return &t;

    }
protected:
    Singleton2() {}
};

template <typename T>
class Singleton
{
public :
    static T *GetInstance()
    {
        if (pSingle == nullptr)
        {
            lock_guard<mutex> lock(mtx);
            if (pSingle == nullptr)
            {
                pSingle = new T();

            }

        }
        return pSingle;

    }
private:
    Singleton() {}
    static T *pSingle;
    static mutex mtx;
};


//mutex Singleton<A>::mtx;
//mutex Singleton<B>::mtx;
//
//A* Singleton<A>::pSingle = nullptr;
//B* Singleton<B>::pSingle = nullptr;
//注释部分代码可以执行,只是每个类都需要单独的两行定义,
//所以使用下面的方法更便捷。
template <typename T>
mutex Singleton<T>::mtx;

template <typename T>
T *Singleton<T>::pSingle = nullptr;

int main()
{

    auto pA = Singleton<A>::GetInstance();
    auto pB = Singleton<B>::GetInstance();

    auto pA2 = Singleton<A>::GetInstance();
    auto pB2 = Singleton<B>::GetInstance();

    cout << pA << endl;
    cout << pB << endl;
    cout << pA2 << endl;
    cout << pB2 << endl;

    return 0;
}
但是上述代码有一个问题,类A和B可以直接构造,和单例的语义不太符合。为此,将A和B类的构造函数设定为private,并声明Singleton<T>为相应的友元函数

#include <iostream>
#include <mutex>
using namespace std;

template <typename T>
class Singleton2
{
public :
    static T *GetInstance()
    {
        static T t;
        return &t;

    }
protected:
    Singleton2() {}
};

template <typename T>
class Singleton
{
public :
    static T *GetInstance()
    {
        if (pSingle == nullptr)
        {
            lock_guard<mutex> lock(mtx);
            if (pSingle == nullptr)
            {
                pSingle = new T();

            }

        }
        return pSingle;

    }
private:
    Singleton() {}
    static T *pSingle;
    static mutex mtx;
};

template <typename T>
mutex Singleton<T>::mtx;
template <typename T>
T *Singleton<T>::pSingle = nullptr;

class A
{
    friend Singleton<A>;
private:
    A() {}
};

class B
{
    friend Singleton<B>;
private:
    B() {}
};


int main()
{

    auto pA = Singleton<A>::GetInstance();
    auto pB = Singleton<B>::GetInstance();

    auto pA2 = Singleton<A>::GetInstance();
    auto pB2 = Singleton<B>::GetInstance();

    cout << pA << endl;
    cout << pB << endl;
    cout << pA2 << endl;
    cout << pB2 << endl;

    return 0;
}


原文链接:https://blog.csdn.net/songchuwang1868/article/details/87882778

猜你喜欢

转载自blog.csdn.net/bandaoyu/article/details/107526369