单例模式

单例模式

单例模式,顾名思义,就是当前Application只有一个实例存在。既然有一个实例,所以我们必须要保证两件事

  • 该类不能有public的构造函数
  • 该类不能够被复制

对于不能够有public的构造函数这种条件,又可以分为两类

  • 只有使用这个类时再去构造(懒汉模式)
  • 当应用程序启动时,这个就已经构造好了(饿汉模式)

饿汉模式

先上代码

// 饿汉模式
class CPPSingleClass1
{
public:
    static CPPSingleClass1* instance()
    {
        return m_single;
    }

    void function1()
    {
        qDebug()<<"CPPSingleClass1::function1"<<endl;
    }

    ~CPPSingleClass1()
    {
        qDebug()<<"CPPSingleClass1 destory"<<endl;
    }

    //....
private:
    CPPSingleClass1(){}
    CPPSingleClass1(const CPPSingleClass1&){}
    CPPSingleClass1& operator = (const CPPSingleClass1&) {}

private:
    static CPPSingleClass1* m_single;
};

CPPSingleClass1* CPPSingleClass1::m_single = new CPPSingleClass1();

懒汉模式

// 懒汉模式
class CPPSingleClass2
{
public:
    static CPPSingleClass2* instance()
    {
        static CPPSingleClass2 cppSingle;
        return &cppSingle;
    }

    void function1()
    {
        qDebug()<<"CPPSingleClass2::function1"<<endl;
    }

    ~CPPSingleClass2()
    {
        qDebug()<<"CPPSingleClass2 Destory"<<endl;
    }


private:
    //c++ 03
    CPPSingleClass2(){}
    CPPSingleClass2(const CPPSingleClass2&){}
    CPPSingleClass2& operator = (const CPPSingleClass2&) {}
};

小结与坑

内存泄露

上面的饿汉模式是有内存泄露的,自己用智能指针取适配下。(这种正常工程中我不推荐用,你们可以忽略

C++11 delete

private中实现构造函数与拷贝构造函数等等已经是03的做法,既然都是modern c++了,可以用 c++ 的 =delete来实现下,比如可以这样

public:
    CPPSingleClass2(const CPPSingleClass2&)=delete;

std::call_once

界面的话,在Qt中UI线程是主线程,多线程的拓展我就不多讲了,
有兴趣的话,可以了解下C++11的 call_once。这个是线程安全的

C++11 静态局部变量

C++11在多线程下的静态局部变量是能够保证线程安全的。所以当前的懒汉模式是工程中常见的用法。

C++的草案原文 6.7节

such a variable is initialized the first time control passes through its declaration; such a variable is considered initialized upon the completion of its initialization. If the initialization exits by throwing an exception, the initialization is not complete, so it will be tried again the next time control enters the declaration. If control enters the declaration concurrently while the variable is being initialized, the concurrent execution shall wait for completion of the initialization. If control re-enters the declaration recursively while the variable is being initialized, the behavior is undefined.

Qt的另一种实现方式 Q_GLOBAL_STATIC

上个栗子

//.h
#ifndef QTSINGLE_H
#define QTSINGLE_H

#include <QGlobalStatic>

class QtSingle
{
public:
    QtSingle(){}
    static QtSingle* instance();

    void functionQt();
};

#endif // QTSINGLE_H


//.cpp
#include "QtSingle.h"

#include <QDebug>
//
Q_GLOBAL_STATIC(QtSingle, single)

QtSingle* QtSingle::instance()
{
    return single;
}

void QtSingle::functionQt()
{
    qDebug()<<"QtSingle...."<<endl;
}

此方法是线程安全的,更多的资料可以看Qt的官方文档

https://doc.qt.io/qt-5/qglobalstatic.html#Q_GLOBAL_STATIC

小结下

优势:
应用程序的生命周期中只有一份实例。不需要频繁初始化,节省资源,方便全局调用。

Qt源码中的单例举例

Qt代码中:Qt代码中,Qt代码中懒汉模式是非常常见的,比如Qt的QFileSystemWatcher的实现就使用了懒汉模式

class QInotifyFileSystemWatcherEngine : public QFileSystemWatcherEngine
{
    Q_OBJECT
public:
    ~QInotifyFileSystemWatcherEngine();
    static QInotifyFileSystemWatcherEngine *create();   //静态的
    //xxxxx
private:
    QInotifyFileSystemWatcherEngine(int fd);        //私有构造函数
    //xxxxx
};

Qt中的这种实现方式,在工程中是相当常见的。这也是主要用法。

代码在这里
https://github.com/CryFeiFei/QtDesignPattern


文章作者: 张小飞
版权声明: 本博客所有文章除特別声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来源 张小飞 !
 上一篇
银雪白熊究极日月807版本的资料 银雪白熊究极日月807版本的资料
经历了从 口袋妖怪吧->口袋改版资源吧->最终的网址! https://jhbenson.lofter.com/post/1f0c17c3_1c5d573dc http://jhbenson.lofter.com/post/1f
2020-07-05
下一篇 
Qt释放线程资源的一些工程上的方法 Qt释放线程资源的一些工程上的方法
Qt官方文档的方法 QThread创建在栈上,然后QObject需要配合QThread释放资源直接上代码。结束的时候线程quit and wait 直接上代码 class Controller : public QObject {
  目录