#include <QMutex>
#include <QMutexLocker>
class Singleton {
private:
// 私有的构造函数确保外部无法直接构造
Singleton() {}
// 禁止拷贝构造函数和赋值操作
Singleton(const Singleton &);
Singleton &operator=(const Singleton &);
// 静态指针用于存储唯一的实例
static Singleton *instance;
// 用于线程安全的互斥锁
static QMutex mutex;
public:
// 公共的静态方法用于获取实例
static Singleton *getInstance() {
if (!instance) {
QMutexLocker locker(&mutex); // 自动锁定,出作用域自动解锁
if (!instance) {
instance = new Singleton;
}
}
return instance;
}
// 示例方法
void doSomething() {
// 实现一些功能
}
};
// 初始化静态成员
Singleton* Singleton::instance = NULL;
QMutex Singleton::mutex;
// 使用示例
int main() {
Singleton *singleton = Singleton::getInstance();
singleton->doSomething();
return 0;
}
这个类中包含了几个重要的组成部分:
-
私有的构造函数和拷贝构造函数:这确保无法在类外部实例化和复制对象。
-
一个静态的私有实例指针和一个公共的静态方法来获取这个实例。如果实例不存在,它将被创建。
-
一个静态的互斥锁
QMutex
来确保线程安全。当第一次调用getInstance
时,会检查实例是否存在,如果不存在,则创建实例。这个检查是在互斥锁的保护下进行的,确保在多线程环境中的安全。
这个实现是一个线程安全的单例模式,可以在多线程环境中安全使用。
懒汉式(Lazy Initialization)和饿汉式(Eager Initialization)是单例模式中两种常见的实现方式,它们主要在实例化对象的时机上有所区别:
-
饿汉式(Eager Initialization):
- 实例化时机:在类加载时就立即初始化,并创建单例对象。
- 优点:没有多线程同步问题,实现简单。
- 缺点:如果自始至终未使用过这个实例,则会造成内存浪费。
-
懒汉式(Lazy Initialization):
- 实例化时机:在真正需要使用对象时才创建这个单例对象,实现了延迟加载。
- 优点:节省资源,只有在使用时才会实例化。
- 缺点:在多线程环境下需要进行额外的同步控制,实现相对复杂。
简单对比:
- 初始化时间:饿汉式在类加载时初始化,而懒汉式在第一次调用时初始化。
- 资源和性能:饿汉式加载类时较慢,但获取对象的速度快,懒汉式加载类时较快,但获取对象的速度慢。
- 线程安全性:饿汉式是线程安全的(因为类加载时已经初始化,不会存在多线程问题),但懒汉式如果没有正确实现同步,则可能在多线程环境下产生多个实例。
在选择使用哪种方式时,需要根据实际应用场景和需求来判断。如果确保应用始终会使用到实例,且关心启动速度,可以选择饿汉式;如果关心资源的有效使用,并且实例不一定会被用到,懒汉式可能更合适。对于懒汉式,在多线程环境下要特别注意线程安全的问题。
饿汉式单例模式
饿汉式单例模式在类加载时就创建实例。以下是其实现示例:
class EagerSingleton {
private:
// 私有构造函数
EagerSingleton() {}
// 禁止拷贝构造函数和赋值操作
EagerSingleton(const EagerSingleton &);
EagerSingleton &operator=(const EagerSingleton &);
// 静态成员变量,类加载时就初始化
static EagerSingleton instance;
public:
// 公共静态方法获取实例
static EagerSingleton& getInstance() {
return instance;
}
// 示例方法
void doSomething() {
// 实现一些功能
}
};
// 初始化静态成员
EagerSingleton EagerSingleton::instance;
懒汉式单例模式
懒汉式单例模式在第一次使用时创建实例。以下是其实现示例:
#include <QMutex>
#include <QMutexLocker>
class LazySingleton {
private:
// 私有构造函数
LazySingleton() {}
// 禁止拷贝构造函数和赋值操作
LazySingleton(const LazySingleton &);
LazySingleton &operator=(const LazySingleton &);
// 静态成员变量,用于存储实例
static LazySingleton *instance;
static QMutex mutex;
public:
// 公共静态方法获取实例
static LazySingleton* getInstance() {
if (!instance) {
QMutexLocker locker(&mutex); // 确保线程安全
if (!instance) {
instance = new LazySingleton;
}
}
return instance;
}
// 示例方法
void doSomething() {
// 实现一些功能
}
};
// 初始化静态成员
LazySingleton* LazySingleton::instance = NULL;
QMutex LazySingleton::mutex;
在这个实现中,LazySingleton
的实例只有在第一次调用getInstance()
时才会被创建。由于这种方式涉及到延迟加载,在多线程环境中需要特别注意线程安全问题,因此使用了QMutex
来确保同步。
对比
- 初始化时机:
- 饿汉式:类加载时立即初始化。
- 懒汉式:第一次使用时初始化。
- 线程安全:
- 饿汉式:由于在程序启动时就已经创建实例,因此天然线程安全。
- 懒汉式:需要适当的同步机制来保证线程安全,例如使用互斥锁。
- 资源和性能:
- 饿汉式:类加载较慢,但获取对象的速度快。
- 懒汉式:类加载较快,但创建实例时可能较慢,尤其是在涉及到同步时。
选择哪一种实现方式取决于具体的应用场景和对性能及资源使用的需求。
https://chat.openai.com/share/15d4edc7-0e39-4148-b781-b2749da72a3d