单例模式再简单不过了!

单例模式

  • [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 是同一个实例
}
相关推荐
捕鲸叉2 小时前
怎样在软件设计中选择使用GOF设计模式
c++·设计模式
啊松同学2 小时前
【Java】设计模式——工厂模式
java·后端·设计模式
捕鲸叉2 小时前
C++设计模式和编程框架两种设计元素的比较与相互关系
开发语言·c++·设计模式
未知陨落3 小时前
数据结构——二叉搜索树
开发语言·数据结构·c++·二叉搜索树
大波V53 小时前
设计模式-参考的雷丰阳老师直播课
java·开发语言·设计模式
一丝晨光3 小时前
gcc 1.c和g++ 1.c编译阶段有什么区别?如何知道g++编译默认会定义_GNU_SOURCE?
c语言·开发语言·c++·gnu·clang·gcc·g++
编程、小哥哥4 小时前
设计模式之装饰器模式(SSO单点登录功能扩展,增加拦截用户访问方法范围场景)
java·设计模式·装饰器模式
汉克老师4 小时前
GESP4级考试语法知识(贪心算法(四))
开发语言·c++·算法·贪心算法·图论·1024程序员节
姆路5 小时前
QT中使用图表之QChart绘制动态折线图
c++·qt
秋说6 小时前
【数据结构 | C++】整型关键字的平方探测法散列
数据结构·c++·算法