单例模式
- [1. 概述](#1. 概述)
- [2. 应用场景](#2. 应用场景)
- [3. 实现细节](#3. 实现细节)
- [4. 实现](#4. 实现)
-
- [4.1 饿汉式(Eager Initialization)](#4.1 饿汉式(Eager Initialization))
- [4.2 懒汉式(Lazy Initialization)](#4.2 懒汉式(Lazy Initialization))
- [4.3 线程安全懒汉式(Thread-safe Lazy Initialization)](#4.3 线程安全懒汉式(Thread-safe Lazy Initialization))
-
- [1. 使用互斥量加锁](#1. 使用互斥量加锁)
- [2. 双重判断](#2. 双重判断)
- [3. 使用 std::call_once](#3. 使用 std::call_once)
- [4.4 静态局部变量实现单例模式(C++11开始)](#4.4 静态局部变量实现单例模式(C++11开始))
1. 概述
确保类在程序中只有一个实例,并提供一个全局访问点。
2. 应用场景
单例模式的使用场景非常广泛,主要集中在需要确保类在程序中只有一个实例,并且提供一个全局访问点的场景。以下是一些具体的使用场景:
-
资源共享 :
数据库连接池 :在程序中,数据库连接是非常宝贵的资源,使用单例模式可以确保整个程序只使用一个数据库连接池实例,从而有效管理数据库连接,避免连接泄露和频繁开启/关闭连接的开销。
线程池:类似于数据库连接池,线程池也是程序中重要的资源。使用单例模式可以确保整个程序只使用一个线程池实例,以便统一管理和调度线程,提高程序的性能和响应速度。 -
配置管理 :
配置文件读取:在程序启动时或运行过程中,可能需要读取配置文件来获取一些必要的配置信息(如数据库连接信息、日志级别等)。使用单例模式可以确保整个程序只使用一个配置类实例来读取和存储这些配置信息,方便程序其他部分随时获取。 -
日志记录 :
在编写日志系统时,使用单例模式可以确保整个程序只使用一个日志类实例来记录日志信息。这样可以保证日志信息的统一性和一致性,便于后续的日志分析和问题追踪。
-
对象缓存 :
对于一些创建成本较高或需要频繁访问的对象,可以使用单例模式进行缓存。通过缓存这些对象,可以提高程序的性能,减少不必要的创建和销毁开销。
-
GUI应用程序 :
在GUI应用程序中,可能需要创建全局唯一的窗口、对话框等界面元素。使用单例模式可以确保这些界面元素在整个程序中只有一个实例,避免重复创建和显示相同的界面元素。
-
状态管理 :
在管理程序的状态(如用户登录状态、程序运行状态等)时,可以使用单例模式来确保状态的一致性和准确性。通过单例模式,可以确保整个程序在访问这些状态时,都是通过一个统一的入口来进行的。
-
资源管理 :
在管理系统资源(如内存、文件等)时,单例模式也可以发挥重要作用。通过单例模式,可以确保资源的分配和释放都是由一个统一的实例来管理的,从而避免资源泄漏或重复释放资源的问题。
3. 实现细节
- 如何确保只有一个实例
默认构造函数私有化,赋值运算符重,防止外部能够创建该类以及其它类继承该类。
确保线程安全,防止多个线程同时创建该类对象。 - 提供全局访问点
4. 实现
总共有四种实现方式:饿汉式、懒汉式、线程安全懒汉式、局部静态变量式
4.1 饿汉式(Eager Initialization)
在程序开始时就创建了唯一的实例,缺点是,如果该单例无需使用,依然会消耗资源 。优点是线程安全的。
cpp
class Singleton {
private:
// 私有构造函数,防止外部创建实例
Singleton() {}
// 静态实例,立即初始化
static Singleton instance;
public:
// 删除拷贝构造和赋值运算符,防止拷贝对象
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
// 静态方法获取单例实例
static Singleton& getInstance() {
return instance;
}
};
// 在类外初始化静态成员
Singleton Singleton::instance;
int main() {
Singleton& s1 = Singleton::getInstance();
Singleton& s2 = Singleton::getInstance();
// s1 和 s2 是同一个实例
}
4.2 懒汉式(Lazy Initialization)
在第一次获取实例时才实例化,所以当无需使用该类情况下可节省内存 ,但是在多线程中可能会出现同时进行实例化的情况,是非线程安全。
cpp
class Singleton {
private:
// 私有构造函数,防止外部创建实例或继承该类
Singleton() {}
// 静态实例,立即初始化
static Singleton instance;
public:
// 删除拷贝构造和赋值运算符,防止拷贝对象
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
// 静态方法获取单例实例
static Singleton& getInstance() {
return instance;
}
};
// 在类外初始化静态成员
Singleton Singleton::instance;
int main() {
Singleton& s1 = Singleton::getInstance();
Singleton& s2 = Singleton::getInstance();
// s1 和 s2 是同一个实例
}
4.3 线程安全懒汉式(Thread-safe Lazy Initialization)
1. 使用互斥量加锁
就是在懒汉式的基础上加锁,使用互斥量确保同一时刻仅有一个线程实例化该对象,但在每次获取实例时都会加锁,影响性能。
cpp
#include <mutex>
class Singleton {
private:
// 私有构造函数
Singleton() {}
// 静态指针,初始化为 nullptr
static Singleton* instance;
// 互斥量
static std::mutex mtx;
public:
// 删除拷贝构造和赋值运算符,防止拷贝对象
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
static Singleton* getInstance() {
// 加锁
std::lock_guard<std::mutex> lock(mtx);
if (instance == nullptr) {
instance = new Singleton();
}
return instance;
}
};
// 初始化静态成员
Singleton* Singleton::instance = nullptr;
std::mutex Singleton::mtx;
int main() {
Singleton* s1 = Singleton::getInstance();
Singleton* s2 = Singleton::getInstance();
// s1 和 s2 是同一个实例
}
2. 双重判断
在加锁前再进行一次判断,双重判断,避免每次都要加锁,提高性能。
cpp
#include <mutex>
class Singleton {
private:
// 私有构造函数
Singleton() {}
// 静态指针,初始化为 nullptr
static Singleton* instance;
// 互斥量
static std::mutex mtx;
public:
// 删除拷贝构造和赋值运算符,防止拷贝对象
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
static Singleton* getInstance() {
if (instance == nullptr) {
// 加锁
std::lock_guard<std::mutex> lock(mtx);
if (instance == nullptr) {
instance = new Singleton();
}
}
return instance;
}
};
// 初始化静态成员
Singleton* Singleton::instance = nullptr;
std::mutex Singleton::mtx;
int main() {
Singleton* s1 = Singleton::getInstance();
Singleton* s2 = Singleton::getInstance();
// s1 和 s2 是同一个实例
}
3. 使用 std::call_once
cpp
#include <mutex>
class Singleton {
private:
Singleton() {}
static Singleton* instance;
static std::once_flag initFlag;
public:
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
static Singleton* getInstance() {
std::call_once(initFlag, []() { instance = new Singleton(); });
return instance;
}
};
// 初始化静态成员
Singleton* Singleton::instance = nullptr;
std::once_flag Singleton::initFlag;
int main() {
Singleton* s1 = Singleton::getInstance();
Singleton* s2 = Singleton::getInstance();
// s1 和 s2 是同一个实例
}
4.4 静态局部变量实现单例模式(C++11开始)
懒加载 :instance
只在第一次调用 getInstance()
时创建,后续直接返回同一个对象。
线程安全 :C++11 保证了静态局部变量的初始化是线程安全的 ,因此无需显式的锁机制。
简洁性 :相比使用锁或 std::call_once
,这种方式的代码更加简洁。
cpp
class Singleton {
private:
// 私有构造函数,防止外部创建实例
Singleton() {}
public:
// 删除拷贝构造和赋值运算符,防止拷贝对象
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
// 静态方法获取单例实例
static Singleton& getInstance() {
static Singleton instance; // 静态局部变量,只有第一次调用时初始化,且线程安全
return instance;
}
};
int main() {
Singleton& s1 = Singleton::getInstance();
Singleton& s2 = Singleton::getInstance();
// s1 和 s2 是同一个实例
}