单例模式

  • 一、核心原理
  • 二、常见的单例模式实现方式
    • 1. 懒汉式(Lazy Initialization)
    • 2. 饿汉式(Eager Initialization)
  • 三、关键实现细节解析
  • 四、单例模式的适用场景与特点
    • 使用场景
      • 日志工具(确保日志写入的唯一性)。
        • spdlog第三方库实现
          • 使用步骤
    • 特点

单例模式是一种常用的设计模式,其核心是确保一个类在全局只有唯一实例,并提供一个全局访问点。

一、核心原理

  1. 限制实例化:通过私有化类的构造函数、拷贝构造函数和赋值运算符,禁止外部直接创建实例或复制实例。
  2. 唯一实例:在类内部维护一个静态的自身实例指针,确保全局只有一个实例。
  3. 全局访问:提供一个静态的公开接口(如 getInstance()),让外部通过该接口获取唯一实例。

二、常见的单例模式实现方式

1. 懒汉式(Lazy Initialization)

实例在第一次被使用时才创建(延迟初始化),节省资源。

#include <QMutex>
#include <QScopedPointer>class Singleton {
public:// 全局访问点:获取唯一实例static Singleton& getInstance() {// 双重检查锁定(DCLP),避免多线程下重复创建if (m_instance.isNull()) {QMutexLocker locker(&m_mutex); // 加锁,确保线程安全if (m_instance.isNull()) {m_instance.reset(new Singleton()); // 首次调用时创建实例}}return *m_instance;}// 示例:单例提供的功能方法void doSomething() {// ... 业务逻辑 ...}private:// 私有化构造函数:禁止外部创建实例Singleton() {}// 私有化拷贝构造和赋值运算符:禁止复制Singleton(const Singleton&) = delete;Singleton& operator=(const Singleton&) = delete;// 静态成员:存储唯一实例static QScopedPointer<Singleton> m_instance;static QMutex m_mutex; // 互斥锁,确保多线程安全
};// 初始化静态成员(类外定义)
QScopedPointer<Singleton> Singleton::m_instance(nullptr);
QMutex Singleton::m_mutex;

2. 饿汉式(Eager Initialization)

实例在程序启动时(类加载时)就创建,避免多线程同步问题,但可能提前占用资源。

class Singleton {
public:// 全局访问点:直接返回预创建的实例static Singleton& getInstance() {static Singleton instance; // 静态局部变量,程序启动时初始化return instance;}void doSomething() {// ... 业务逻辑 ...}private:// 私有化构造函数Singleton() {}// 禁止复制Singleton(const Singleton&) = delete;Singleton& operator=(const Singleton&) = delete;
};

三、关键实现细节解析

  1. 私有化构造函数
    private 权限的构造函数阻止外部通过 new Singleton()Singleton obj 创建实例,确保实例只能在类内部创建。

  2. 禁止复制和赋值
    通过 = delete 显式删除拷贝构造函数和赋值运算符,避免外部通过 Singleton obj = Singleton::getInstance() 复制实例,保证唯一性。

  3. 静态实例与全局访问
    类内部的静态成员(m_instance 或静态局部变量)存储唯一实例,getInstance() 静态方法提供全局访问入口,确保任何地方都能获取同一个实例。

  4. 线程安全处理

    • 懒汉式中使用 QMutex 加锁,避免多线程同时调用 getInstance() 时创建多个实例(双重检查锁定进一步优化性能)。
    • 饿汉式依赖静态变量的初始化特性(C++11 后静态局部变量初始化是线程安全的),无需额外加锁。

四、单例模式的适用场景与特点

使用场景

  • 全局配置管理(如程序的配置类)。
  • 设备管理器(如硬件设备的唯一控制实例)。
  • 缓存管理器(避免重复创建缓存对象)。

日志工具(确保日志写入的唯一性)。

//ErrorLogger.h文件
#ifndef ERRORLOGGER_H
#define ERRORLOGGER_H#include <QString>
#include <QMutex>// 错误日志工具类(单例模式)
class ErrorLogger
{
public:// 获取单例实例static ErrorLogger& getInstance();// 禁止拷贝和赋值ErrorLogger(const ErrorLogger&) = delete;ErrorLogger& operator=(const ErrorLogger&) = delete;// 写入错误日志void writeLog(const QString& errorMessage);// 设置日志文件路径(默认当前目录下的error.log)void setLogFilePath(const QString& path);private:// 私有构造函数(单例模式)ErrorLogger();QString m_logFilePath; // 日志文件路径QMutex m_mutex;       // 互斥锁,确保多线程安全
};#endif // ERRORLOGGER_H//ErrorLogger.cpp文件
#include "ErrorLogger.h"
#include <QFile>
#include <QTextStream>
#include <QDateTime>
#include <QDir>
#include <QDebug>ErrorLogger::ErrorLogger()
{// 默认日志路径:当前程序目录下的error.logm_logFilePath = QDir::currentPath() + "/error.log";
}ErrorLogger& ErrorLogger::getInstance()
{static ErrorLogger instance;return instance;
}void ErrorLogger::setLogFilePath(const QString& path)
{m_logFilePath = path;
}void ErrorLogger::writeLog(const QString& errorMessage)
{// 多线程加锁,避免日志写入冲突QMutexLocker locker(&m_mutex);// 获取当前时间戳(格式:yyyy-MM-dd hh:mm:ss)QString timeStamp = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss");// 构建日志内容(时间 + 错误信息)QString logContent = QString("[%1] Error: %2\n").arg(timeStamp).arg(errorMessage);// 打开文件(以追加模式,不存在则创建)QFile file(m_logFilePath);if (!file.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text)) {qDebug() << "无法写入日志文件:" << file.errorString();return;}// 写入日志QTextStream out(&file);out << logContent;file.close();
}//main.cpp文件
// 写入错误日志(包含系统错误信息)ErrorLogger::getInstance().writeLog(QString("文件打开失败: %1(路径:%2)").arg(file.errorString()).arg(file.fileName()));
// 可选:设置自定义日志路径(如程序数据目录)ErrorLogger::getInstance().setLogFilePath(QDir::homePath() + "/myapp/logs/error.log");
spdlog第三方库实现

链接: C++日志记录库SPDLog简介
spdlog学习—安装及基本使用
spdlog是一个高性能、超快速、零配置的C++日志库,它旨在提供简洁的API和丰富的功能,同时保持高性能的日志记录。它支持多种输出目标、格式化选项、线程安全以及异步日志记录。

  • 高性能:spdlog专为速度而设计,即使在高负载情况下也能保持良好的性能
  • 零配置:无需复杂的配置,只需包含头文件即可在项目中使用
  • 异步日志:支持异步日志记录,减少对主线程的影响
  • 格式化:支持自定义日志消息的格式化,包括时间戳、线程ID、日志级别等
  • 多平台:跨平台兼容,支持Windows、Linux、MacOS等操作系统
  • 丰富的API:提供丰富的日志级别和操作符重载,方便记录各种类型的日志
  • 多目标输出:可以将日志输出到控制台、普通文本文件、循环写入文件(rotating log files)、每日生成新文件(daily logs)、系统日志等目标,同时也支持异步写入以提高性能。
  • 丰富的日志级别:Spdlog 支持常见的日志级别,如 TRACE、DEBUG、INFO、WARN、ERROR、CRITICAL 等,用户可以根据需要选择不同级别的日志输出。
  • 条件日志:根据预定义的条件开关,可以动态启用或禁用特定级别的日志输出。
使用步骤

spdlog库的使用也非常简单,只需要下载源代码,然后把根目录下的include目录下的文件拷贝到我们的工程下,在工程中包含相应的头文件即可。

  • 控制台打印
  #include <spdlog/spdlog.h>#include <string.h>#include <iostream>int main(){// 普通打印spdlog::info("Welcome to info spdlog!");// 格式化打印// 打印字符串spdlog::info("Hello World {}", "spdlog!");// 打印数字spdlog::error("spdlog errCode : {}", -10020);// 指定打印数字的占位符spdlog::warn("spdlog format char {:08d}", 12);// 格式化打印不同进制的数据spdlog::critical("Support for int:{0:d} hex:{0:x} oct:{0:o} bin:{0:b}", 42);// 打印浮点型数据spdlog::info("float args are {:03.2f}", 1.23456);// 打印多个参数spdlog::info("string args are {0} {1}..", "too", "supported");spdlog::info("number args are {0} {1} {2}..", 10020, 10040, -100);system("pause");}

在这里插入图片描述

  • 在文件中打印日志
  #include <spdlog/spdlog.h>#include <spdlog/sinks/basic_file_sink.h>#include <string.h>#include <iostream>int main(){try{// 参数1 日志标识符, 参数2 日志文件名std::shared_ptr<spdlog::logger> mylogger = spdlog::basic_logger_mt("spdlog", "spdlog.log");// 设置日志格式. 参数含义: [日志标识符] [日期] [日志级别] [线程号] [数据]mylogger->set_pattern("[%n][%Y-%m-%d %H:%M:%S.%e] [%l] [%t]  %v");mylogger->set_level(spdlog::level::debug);spdlog::flush_every(std::chrono::seconds(5)); // 定期刷新日志缓冲区mylogger->trace("Welcome to info spdlog!");mylogger->debug("Welcome to info spdlog!");mylogger->info("Welcome to info spdlog!");mylogger->warn("Welcome to info spdlog!");mylogger->error("Welcome to info spdlog!");mylogger->critical("Welcome to info spdlog!");// 刷新mylogger->flush_on(spdlog::level::debug);}catch (const spdlog::spdlog_ex& ex){std::cout << "Log initialization failed: " << ex.what() << std::endl;}system("pause");}

执行结果。执行程序后就会在当前目录下生成一个spdlog.log文件,看下打印内容
在这里插入图片描述

特点

  • 优点
    确保全局唯一实例,减少资源消耗(如频繁创建销毁实例的开销),提供统一的访问点。

  • 缺点
    单例本质是全局变量,可能导致代码耦合度升高;测试困难(单例状态难以隔离);在多线程环境下需谨慎处理同步问题。

通过上述实现,单例模式能有效控制类的实例数量,在需要全局唯一访问点的场景中非常实用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/96072.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/96072.shtml
英文地址,请注明出处:http://en.pswp.cn/pingmian/96072.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

stm32 链接脚本没有 .gcc_except_table 段也能支持 C++ 异常

stm32 使用 cubemx 生成的 gnu ld 链接脚本没有 .gcc_except_table 段。如下所示 /* ****************************************************************************** ** ** file : LinkerScript.ld ** ** author : Auto-generated by STM32CubeIDE ** ** Abst…

SpringBoot改造MCP服务器(StreamableHTTP)

项目地址&#xff1a; https://gitee.com/kylewka/smart-ai 1 项目说明 MCP&#xff08;Model Context Protocol&#xff09;协议是一个用于 AI 模型和工具之间通信的标准协议。随着 AI 应用变得越来越复杂并被广泛部署&#xff0c;原有的通信机制面临着一系列挑战。 近期 MCP …

【数学建模】烟幕干扰弹投放策略优化:模型与算法整合框架

烟幕干扰弹投放策略优化&#xff1a;模型与算法整合框架 基于文献研究和问题需求分析&#xff0c;我们构建了完整的模型与算法整合框架。 一、整体建模框架 1. 核心问题分解 物理层&#xff1a;烟幕弹道运动与扩散特性建模博弈层&#xff1a;导弹识别与决策机制建模优化层&…

结合大数据知识体系对仓库建模方法总结

传统的仓库建模理论&#xff08;如维度建模&#xff09;仍然是基石&#xff0c;但大数据的“4V”特性&#xff08;Volume, Velocity, Variety, Value&#xff09;要求我们对这些方法进行演进和补充。 以下是结合大数据知识体系对仓库建模方法的总结&#xff1a;一、核心目标&am…

C 语言第一课:hello word c

C 语言第一课&#xff1a;hello word c开发工具创建项目快速学习平台开发工具 个人推荐使用 jetBrains 公司的 CLion 开发工具下载地址 https://www.jetbrains.com/clion/ 创建项目 编写代码 //头文件 #include <stdio.h>//程序入口 int main(){printf("hello w…

基于Java Spring Boot的云原生TodoList Demo 项目,验证云原生核心特性

以下是一个基于 Java Spring Boot 的云原生 TodoList Demo 项目&#xff0c;涵盖 容器化、Kubernetes 编排、CI/CD、可观测性、弹性扩缩容 等核心云原生特性&#xff0c;代码简洁且附详细操作指南&#xff0c;适合入门学习。项目概览 目标&#xff1a;实现一个支持增删改查&…

开源一个轻量级 Go 工具库:go-commons

项目背景 在日常 Go 开发中&#xff0c;我们经常需要处理字符串操作和系统监控相关的功能。虽然 Go 标准库提供了基础的字符串处理能力&#xff0c;但在实际项目中&#xff0c;我们往往需要一些更便捷的工具函数来提高开发效率。 基于"尽可能不使用第三方依赖"的原…

clang(clangd)与arm-linux-gcc、ARMGCC、ICCARM(IAR)、C51编译器的兼容性

环境&#xff1a;vscodeclangdEIDE开发开发单片机&#xff08;C51 keilMDK IAR&#xff09;。 vscode远程clangdarm-linux-gcc(交叉编译工具链)。 &#xff08;1&#xff09;首先clang&#xff08;clangd&#xff09;是兼容gcc的&#xff0c;也就是兼容arm-linux-gcc&#xff…

Docker 部署 Rancher2.4.4

获取2.4.4镜像包docker pull rancher/rancher:v2.4.4创建目录并赋予权限mkdir -p /home/rancher/{data,log} && chmod -R 777 /home/rancher启动容器docker run -d \ --privileged \ --name rancher \ --restartunless-stopped \ -p 80:80 -p 443:443 \ -v /home/ranc…

无root使用adb模式下的scene 用shizuku激活scene教程

本次教程是用shizuku和ShizukuRunner激活scene的adb模式&#xff0c;实现大部分功能&#xff0c;比较简单&#xff0c;如果手机已经root直接使用root模式即可。 工具 scene(点我下载) Shizuku(点我下载) ShizukuRunner(点我下载) 教程 1.首先要有一台支持无线调试的手机(安…

《UE5_C++多人TPS完整教程》学习笔记50 ——《P51 多人游戏中的俯仰角(Pitch in Multiplayer)》

本文为B站系列教学视频 《UE5_C多人TPS完整教程》 —— 《P51 多人游戏中的俯仰角&#xff08;Pitch in Multiplayer&#xff09;》 的学习笔记&#xff0c;该系列教学视频为计算机工程师、程序员、游戏开发者、作家&#xff08;Engineer, Programmer, Game Developer, Author&…

树莓派 Ubuntu 24.04 开机换源总结

1. 图形界面 (桌面版) 如果你刷的是 Ubuntu Desktop 24.04&#xff1a;打开 Software & Updates&#xff08;软件和更新&#xff09;。在 Ubuntu Software 标签里找到 Download from 下拉菜单。默认只有 Main server 和 Server for China&#xff0c;如果想要更多选择&…

工业显示器在地铁电力监控与运维中的应用

在地铁电力监控与运维中&#xff0c;工业显示器凭借其高可靠性、环境适应性和强大的功能集成&#xff0c;成为保障地铁供电系统安全稳定运行的核心设备。以下从关键应用场景、技术优势及实际案例三个维度展开分析&#xff1a;一、核心应用场景变配电室与环控电控室监控 工业显示…

Docker 快速部署单节点 NiFi 1.27

Docker 快速部署单节点 NiFi 1.27 前言 Apache NiFi 是一款强大的数据集成工具&#xff0c;专注于数据的采集、处理和分发&#xff0c;具有可视化流程设计、强大的容错能力等特点。通过 Docker 部署可以快速搭建环境&#xff0c;省去复杂的配置步骤。本文介绍如何使用官方镜像…

php redis 中文API文档手册

php redis 中文API文档手册 Redis::__construct构造函数 $redis new Redis();connect, open 链接redis服务 参数 host: string&#xff0c;服务地址 port: int,端口号 timeout: float,链接时长 (可选, 默认为 0 &#xff0c;不限链接时间) 注: 在redis.conf中也有时间&#xf…

Windows环境下实现GitLab与Gitee仓库代码提交隔离

1. 背景 在开发工作中&#xff0c;我需要同时使用2个代码托管平台&#xff1a;公司统一使用的GitLab和个人学习用的 Gitee。我希望能够在同一台电脑上方便地管理和提交两个平台的代码&#xff0c;实现账号和提交内容的有效隔离。 前提条件&#xff1a; 已安装Git Bash、Tort…

深度解析:抗辐射电源芯片 ASP4644S2B 在空间环境中的单粒子效应表现

摘要&#xff1a;随着航天技术的飞速发展&#xff0c;空间电子设备面临着日益复杂和严苛的辐射环境挑战。单粒子效应&#xff08;SEE&#xff09;作为辐射环境对半导体器件影响的主要形式之一&#xff0c;极大地影响着航天电子系统的可靠性和稳定性。本文通过系统梳理国科安芯推…

【RabbitMQ】如何在 Ubuntu 安装 RabbitMQ

1. 安装部署 Erlang 环境 RabbitMQ 是一套开源的消息队列服务软件&#xff0c;基于 Erlang 语言编写的&#xff0c;因此&#xff0c;在安装 RabbitMQ 之前&#xff0c;我们需要先部署 Erlang 环境&#xff0c;再安装 RabbitMQ 环境&#xff08;就像运行 Java 程序&#xff0c;…

vue集成高德地图API工具类封装

import axios, { AxiosInstance, AxiosResponse } from axios;// 高德地图 API 响应基础结构 interface AMapResponse {status: string;info: string;infocode: string; }// 逆地理编码响应结构 interface RegeoResponse extends AMapResponse {regeocode: {formatted_address:…

手写 Tomcat

文章目录02 初出茅庐:构造一个极简的 HttpServerRequestResponseHttpServer03 动态 Response : 按照规范构造返回流04 各司其职的 Server : 拆分响应模块与处理模块HttpConnectorHttpProcessor05 Server 性能提升: 设计多个 ProcessorHttpConnectorHttpProcessor06 规范化: 引入…