zoukankan      html  css  js  c++  java
  • Qt 实现单例模式

    单例模式

      单利模式作为一种常用的软件设计模式,主要是用来保证系统中只有一个实例,例如一般一个程序中只有一个日志输出实例,一个系统中只有一个数据库连接实例,这时候用单例模式非常合适。

    简单的单例模式

     1 class QSingleton
     2 {
     3 public:
     4     static QSingleton* instance()
     5     {
     6         if (m_pInstance == NULL)
     7         {
     8             m_pInstance = new QSingleton();
     9         }
    10         return m_pInstance;
    11     }
    12 
    13     static void Release()
    14     {
    15         if (m_pInstance != NULL)
    16         {
    17             delete m_pInstance;
    18             m_pInstance = NULL;
    19         }
    20     }
    21 private:
    22     QSingleton(){}
    23     QSingleton(const QSingleton&){}
    24     QSingleton& operator==(const QSingleton&){}
    25 private:
    26     static QSingleton* m_pInstance;
    27 };
    28 // 静态成员变量需要在类体的外面进行初始化
    29 QSingleton* QSingleton::m_pInstance = NULL;

      上面的实现一种最简单的单利模式,是一种懒汉模式,所谓的懒汉模式就是在程序需要时才进行成员变量的创建也就是“延时加载”,与之相对的就是饿汉模式,恶汉模式就是在程序启动时就需要创建变量。懒汉模式是时间换空间,恶汉模式是空间换时间,看如下恶汉模式的一个简单实现:

     1 class QSingleton
     2 {
     3 public:
     4     static QSingleton* instance()
     5     {
     6         return m_pInstance;
     7     }
     8 
     9     static void Release()
    10     {
    11         if (m_pInstance != NULL)
    12         {
    13             delete m_pInstance;
    14             m_pInstance = NULL;
    15         }
    16     }
    17     QSingleton(){}
    18 
    19 private:
    20     QSingleton(const QSingleton&){}
    21     QSingleton& operator==(const QSingleton&){}
    22 private:
    23     static QSingleton* m_pInstance;
    24 };
    25 
    26 // 直接初始化静态成员变量
    27 QSingleton* QSingleton::m_pInstance = new QSingleton;

      因为程序启动时,就需要创建对象,所以单例类的默认构造函数就需要时public的,此时用户就能够创建单例类的对象,从而就不能保证单例模式的初衷:一个程序只有一个实例类,另外当我们的单例类的默认构造函数需要参数时,并且改参数需要在程序执行过程中才能够构造,此时就不能用饿汉模式的单例模式。因此下面着重对懒汉模式的单例模式实现做讨论。上面的简单的懒汉模式的单例类实现有如下缺点:

      每次都得判断m_pInstance是否为空,增加了程序开销,而饿汉模式没有此问题。
      需要手动调用Release函数释放静态成员变量分配内存,上面的饿汉模式也有此问题。针对此问题我们可以通过智能指针来避免。
      不是线程安全的,要想在多线程环境下安全使用,就需要在程序一开始处,其他线程还未创建时,调用一次instance函数,但这样就抛弃了懒汉模式延迟加载的优点。饿汉模式因为在程序一开始就创建了对象,因此是线程安全的。
    线程安全的单例模式

     1 class QSingleton
     2 {
     3 public:
     4     static QSharedPointer<QSingleton>& instance()
     5     {
     6         QMutexLocker mutexLocker(&m_Mutex);
     7         if (m_pInstance.isNull())
     8         {
     9             m_pInstance = QSharedPointer<QSingleton>(new QSingleton());
    10         }
    11         return m_instance;
    12     }
    13 private:
    14     QSingleton(){}
    15     QSingleton(const QSingleton&){}
    16     QSingleton& operator==(const QSingleton&){}
    17 private:
    18     static QMutex m_Mutex;
    19     static QSharedPointer<QSingleton> m_pInstance;
    20 };
    21 
    22 QMutex QSingleton::m_Mutex;
    23 QSharedPointer<QSingleton> QSingleton::m_pInstance;

      通过智能指针来管理成员变量,保证了在程序退出时,自动释放内存,通过加锁保证了m_pInstance创建的唯一性,但是因为程序每次调用instance就需要先加锁,大大增加了程序开销,看如下改进实现:

     1 class QSingleton
     2 {
     3 public:
     4     static QSharedPointer<QSingleton>& instance()
     5     {
     6 
     7         if (m_pInstance.isNull())
     8         {
     9             QMutexLocker mutexLocker(&m_Mutex);
    10             if (m_pInstance.isNull())
    11                 m_pInstance = QSharedPointer<QSingleton>(new QSingleton());
    12         }
    13         return m_pInstance;
    14     }
    15 private:
    16     QSingleton(){}
    17     QSingleton(const QSingleton&){}
    18     QSingleton& operator==(const QSingleton&){}
    19 private:
    20     static QMutex m_Mutex;
    21     static QSharedPointer<QSingleton> m_pInstance;
    22 };
    23 
    24 QMutex QSingleton::m_Mutex;
    25 QSharedPointer<QSingleton> QSingleton::m_pInstance;

      上面的实现通过两次检查成员变量是否为空(double-check),避免了每次调用instance函数就锁定的效率问题。

    Meyers提出的一种单例模式的实现

     1 class QSingleton
     2 {
     3 public:
     4     static QSingleton& instance()
     5     {
     6         static QSingleton qinstance;
     7         return qinstance;
     8     }
     9 private:
    10     QSingleton(){}
    11     QSingleton(const QSingleton&){}
    12     QSingleton& operator==(const QSingleton&){}
    13 };

      在上述单例模式的实现中,在instance函数中声明static的局部变量,因为静态变量在程序中只会分配一次内存,保证了实例的唯一性,并且作为局部变量只有在程序第一次调用的时候才会初始化,也实现了延迟加载,而且因为不是指针变量,在程序结束时会自动回收内存,几乎就是完美的实现。虽然是只分配一次内存,但就能够确保线程安全吗?答案是否定的,因为c++的构造函数本身就不是线程安全的,当我们在构造函数内部初始化成员变量或者全局变量时,时间片就有可能被切走,我们在使用时,这一点尤为重要。

  • 相关阅读:
    linux driver ------ 交叉工具链(cross toolchain)
    Qt ------ 截图、获取鼠标指定的RGB值
    Qt ------ QWidget 自定义子类使用信号与槽(Q_OBJECT)后 stylesheet 失效
    Qt error ------ incomplete type 'QApplication' used in nested name specifier
    Qt ------ Q_UNUSED
    SpringCloud 组件Eureka参数配置项详解
    过滤器(Filter)与拦截器(Interceptor)的区别
    事务隔离级别
    事务四大特性
    get与post的区别
  • 原文地址:https://www.cnblogs.com/ybqjymy/p/14918524.html
Copyright © 2011-2022 走看看