单例模式(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;
上⾯的实现⼀种最简单的单利模式,是⼀种懒汉模式,所谓的懒汉模式就是在程序需要时才进⾏成员
变量的创建也就是“延时加载”,与之相对的就是饿汉模式,恶汉模式就是在程序启动时就需要创建变量。懒汉模式是时间换空间,恶汉模式是空间换时间,看如下恶汉模式的⼀个简单实现:
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函数,但这样就抛弃了懒汉模式延迟加载的优点。饿汉模式因为在程序⼀开始就创建了对象,因此是线程安全的。
线程安全的单例模式
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提出的⼀种单例模式的实现
static QSingleton& instance()
{
static QSingleton qinstance;
return qinstance;
}
private:
QSingleton(){}
QSingleton(const QSingleton&){}
QSingleton& operator==(const QSingleton&){}
};
在上述单例模式的实现中,在instance函数中声明static的局部变量,因为静态变量在程序中只会分配
⼀次内存,保证了实例的唯⼀性,并且作为局部变量只有在程序第⼀次调⽤的时候才会初始化,也实现了延迟加载,⽽且因为不是指针变量,在程序结束时会⾃动回收内存,⼏乎就是完美的实现。虽然是只分配⼀次内存,但就能够确保线程安全吗?答案是否定的,因为c++的构造函数本⾝就不是线程安全的,当我们在构造函数内部初始化成员变量或者全局变量时,时间⽚就有可能被切⾛,我们在使⽤时,这⼀点尤为重要。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论