单例模式(Qt实现)

单例模式

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

简单的单例模式

class QSingleton
{
public:
    static QSingleton* instance()
    {
        if (m_pInstance == NULL)
        {
            m_pInstance = new QSingleton();
        }
        return m_pInstance;
    }

    static void Release()
    {
        if (m_pInstance != NULL)
        {
            delete m_pInstance;
            m_pInstance = NULL;
        }
    }
private:
    QSingleton(){}
    QSingleton(const QSingleton&){}
    QSingleton& operator==(const QSingleton&){}
private:
    static QSingleton* m_pInstance;
};
// 静态成员变量需要在类体的外面进行初始化
QSingleton* QSingleton::m_pInstance = NULL;

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

class QSingleton
{
public:
    static QSingleton* instance()
    {
        return m_pInstance;
    }

    static void Release()
    {
        if (m_pInstance != NULL)
        {
            delete m_pInstance;
            m_pInstance = NULL;
        }
    }
    QSingleton(){}

private:
    QSingleton(const QSingleton&){}
    QSingleton& operator==(const QSingleton&){}
private:
    static QSingleton* m_pInstance;
};

// 直接初始化静态成员变量
QSingleton* QSingleton::m_pInstance = new QSingleton;

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

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

线程安全的单例模式

class QSingleton
{
public:
    static QSharedPointer<QSingleton>& instance()
    {
        QMutexLocker mutexLocker(&m_Mutex);
        if (m_pInstance.isNull())
        {
            m_pInstance = QSharedPointer<QSingleton>(new QSingleton());
        }
        return m_instance;
    }
private:
    QSingleton(){}
    QSingleton(const QSingleton&){}
    QSingleton& operator==(const QSingleton&){}
private:
    static QMutex m_Mutex;
    static QSharedPointer<QSingleton> m_pInstance;
};

QMutex QSingleton::m_Mutex;
QSharedPointer<QSingleton> QSingleton::m_pInstance;

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

class QSingleton
{
public:
    static QSharedPointer<QSingleton>& instance()
    {

        if (m_pInstance.isNull())
        {
            QMutexLocker mutexLocker(&m_Mutex);
            if (m_pInstance.isNull())
                m_pInstance = QSharedPointer<QSingleton>(new QSingleton());
        }
        return m_pInstance;
    }
private:
    QSingleton(){}
    QSingleton(const QSingleton&){}
    QSingleton& operator==(const QSingleton&){}
private:
    static QMutex m_Mutex;
    static QSharedPointer<QSingleton> m_pInstance;
};

QMutex QSingleton::m_Mutex;
QSharedPointer<QSingleton> QSingleton::m_pInstance;

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

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

class QSingleton
{
public:
    static QSingleton& instance()
    {
        static QSingleton qinstance;
        return qinstance;
    }
private:
    QSingleton(){}
    QSingleton(const QSingleton&){}
    QSingleton& operator==(const QSingleton&){}
};

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

Qt是一个功能强大的C++跨平台应用程序开发框架。Qt中提供了单例模式实现方法,在Qt实现一个单例需要使用Q_GLOBAL_STATIC宏来创建单例,并使用connect()连接信号和槽。 全局信号是指可以被应用程序中的任何对象接收的信号。由于应用程序中的对象可以在应用程序的不同线程中运行,因此使用单例模式实现全局信号是一个很好的解决方案。 单例模式是一种创建单个实例的方式,确保只有一个实例被创建并在应用程序的整个生命周期中使用。Qt中使用Q_GLOBAL_STATIC宏定义单例,该宏的第一个参数是单例类的类型。 例如,下面的代码定义了一个名为MySingleton的单例类: ``` class MySingleton { public: static MySingleton* getInstance(); private: MySingleton(); Q_DISABLE_COPY(MySingleton) }; MySingleton *MySingleton::getInstance() { static const QScopedPointer<MySingleton> instance(new MySingleton()); return instance.data(); } typedef QGlobalStatic<MySingleton> MySingletonPtr; Q_GLOBAL_STATIC(MySingletonPtr, mySingleton) ``` 全局信号可以使用Qt中的QObject类来实现。QObject类中定义了signal和slot,可以在应用程序中的不同对象之间传递消息和数据。 为了实现全局信号,可以在单例类中添加一个QObject对象,并在其中定义信号和槽。在接收信号的对象中,可以使用connect()函数将对象的槽函数连接到全局信号。 例如,下面的代码向MySingleton类中添加了一个QObject对象,并定义一个全局信号: ``` class MySingleton : public QObject { public: static MySingleton* getInstance(); signals: void mySignal(); private: MySingleton(); Q_DISABLE_COPY(MySingleton) }; MySingleton *MySingleton::getInstance() { static const QScopedPointer<MySingleton> instance(new MySingleton()); return instance.data(); } typedef QGlobalStatic<MySingleton> MySingletonPtr; Q_GLOBAL_STATIC(MySingletonPtr, mySingleton) MySingleton::MySingleton() : QObject() {} ``` 现在,就可以在应用程序中的其他对象中接收全局信号: ``` MySingleton::getInstance()->emit mySignal(); ``` 可以将mySignal()信号连接到任何具有匹配参数的槽函数中: ``` connect(MySingleton::getInstance(), &MySingleton::mySignal, this, &MyObject::mySlot); ``` 使用单例模式和QObject类,Qt可以非常方便地实现全局信号。通过这种方式,任何对象都可以接收应用程序中发生的事件,从而更加灵活和可扩展。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值