《C++设计模式》单例模式

news2025/1/5 17:12:22

文章目录

  • 1、简介
  • 2、单例模式的种类
    • 2.1 饿汉式单例模式:
    • 2.2 懒汉式单例模式:
  • 3、单例模式的具体介绍
    • 3.1、饿汉式
      • 3.1.1、代码示例
      • 3.1.2、组成部分
      • 3.1.3、优缺点
      • 3.1.4、应用场景
    • 3.2、懒汉式
      • 3.2.1、代码示例
      • 3.2.2、组成部分
      • 3.2.3、优缺点
      • 3.2.4、应用场景
  • 4、面试常问问题
  • 5、总结
  • 6、参考文章

1、简介

单例模式(Singleton Pattern)是一种创建型设计模式,它确保一个类只有一个实例,并提供一个全局访问点来获取该实例。这个模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。
以下是单例模式的一些关键点
(1)私有构造方法:为了防止外部代码通过new关键字创建类的多个实例,单例模式的构造方法是私有的。
(2)静态变量:类的唯一实例通常存储在一个静态变量中,这样它可以被类的所有实例共享和访问。
(3)公共静态方法:为了提供对唯一实例的全局访问,单例模式通常包含一个公共的静态方法,该方法在需要时返回类的唯一实例。如果实例尚不存在,该方法会创建它;如果实例已经存在,则直接返回该实例。
(4)线程安全:在多线程环境中,需要确保单例模式的实现是线程安全的,以防止多个线程同时创建类的多个实例。这通常通过同步机制来实现。

2、单例模式的种类

2.1 饿汉式单例模式:

(1)在类加载时就创建实例。
(2)线程安全,因为实例在类加载时就已经存在,不存在多线程竞争问题。
(3)可能导致内存浪费,因为即使从未使用过该实例,它仍然会被创建。
(4)实现简单,通常通过静态变量和静态代码块来完成。

2.2 懒汉式单例模式:

(1)在第一次使用时才创建实例。
(2)非线程安全,因为多个线程可能同时进入创建实例的代码块,导致创建多个实例(除非添加额外的同步机制)。
(3)节省内存资源,因为实例只在需要时才创建。
(4)实现相对复杂,需要处理线程安全问题,通常通过同步方法或双重检查锁定来实现。

3、单例模式的具体介绍

3.1、饿汉式

3.1.1、代码示例

#include <iostream>

class Singleton {
public:
    // 禁用拷贝构造函数和赋值运算符,确保单例的唯一性
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;

    // 提供一个公共的静态方法来获取单例对象
    static Singleton& getInstance() {
        static Singleton instance; // 静态局部变量,只在第一次调用时构造
        return instance;
    }

    // 一个示例方法,用于展示单例对象的使用
    void doSomething() {
        std::cout << "Doing something in Singleton instance!" << std::endl;
    }

private:
    // 私有构造函数,防止外部直接创建实例
    Singleton() {
        std::cout << "Singleton instance created!" << std::endl;
    }

    // 禁用析构函数,防止外部调用delete(实际上在程序结束时会自动调用)
    // 注意:对于饿汉式单例,析构函数通常不需要特别处理,因为实例会在程序结束时自动销毁
    // 但为了完整性,这里仍然声明为私有,并添加注释
    ~Singleton() = default;
};

int main() {
    // 获取单例对象并调用其方法
    Singleton& singleton = Singleton::getInstance();
    singleton.doSomething();

    // 尝试获取同一个单例对象(实际上仍然是同一个实例)
    Singleton& anotherSingleton = Singleton::getInstance();
    anotherSingleton.doSomething();

    // 由于拷贝构造函数和赋值运算符被禁用,以下代码会导致编译错误
    // Singleton copySingleton = Singleton::getInstance(); // 错误:拷贝构造函数被删除
    // Singleton assignSingleton;
    // assignSingleton = Singleton::getInstance(); // 错误:赋值运算符被删除

    return 0;
}

运行结果:

Singleton instance created!
Doing something in Singleton instance!
Doing something in Singleton instance!

3.1.2、组成部分

(1)私有静态成员变量:
这是一个静态的类成员变量,用于存储单例实例。由于它是私有的,所以外部类不能直接访问或修改它。
(2)私有构造函数:
类的构造函数被声明为私有,以防止外部类通过new关键字创建新的实例。这是实现单例模式的关键之一。
(3)公共静态方法:
提供一个公共的静态方法(通常是getInstance),用于返回单例实例。这个方法检查静态成员变量是否已经持有实例,如果还没有,则创建一个新的实例并返回;如果已经存在,则直接返回该实例。
(4)静态初始化块(可选):
在某些情况下,单例实例的初始化可能需要执行一些复杂的逻辑,这时可以使用静态初始化块来完成。然而,在饿汉模式中,由于实例在类加载时就已经被创建,所以通常不需要静态初始化块。

3.1.3、优缺点

(1) 优点:线程安全,实现简单。
(2) 缺点:可能导致内存浪费,因为即使从未使用过该实例,它仍然会被创建。

3.1.4、应用场景

(1)线程安全需求高:由于饿汉模式在类加载时就创建了实例,所以它是线程安全的,无需额外的同步机制。这在多线程环境中尤其有用,可以避免竞争条件和潜在的线程安全问题。
(2)实例创建开销小:如果实例的创建开销很小,或者实例的创建和初始化过程不会消耗太多资源,那么饿汉模式是一个很好的选择。因为即使实例在类加载时被创建,也不会对系统性能产生显著影响。
(3)实例需要提前准备:在某些情况下,实例需要在类加载时就准备好,以便在后续的代码执行过程中随时使用。例如,某些配置信息或资源需要在应用程序启动时就被加载和初始化。

3.2、懒汉式

3.2.1、代码示例

#include <iostream>
#include <mutex>
#include <memory>

class Singleton {
public:
    // 禁用拷贝构造函数和赋值运算符
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;

    // 获取单例实例的静态方法
    static Singleton* getInstance() {
        // 使用双重检查锁定(Double-Checked Locking)来确保线程安全和性能
        if (instance_ == nullptr) {
            std::lock_guard<std::mutex> lock(mutex_);
            if (instance_ == nullptr) {
                instance_ = new Singleton();
            }
        }
        return instance_;
    }

    // 示例方法
    void doSomething() {
        std::cout << "Singleton instance is doing something!" << std::endl;
    }

    // 析构函数,用于清理资源
    ~Singleton() {
        std::cout << "Singleton instance is being destroyed!" << std::endl;
    }

private:
    // 私有构造函数,防止外部创建实例
    Singleton() {
        std::cout << "Singleton instance is being created!" << std::endl;
    }

    // 静态成员变量,保存单例实例
    static Singleton* instance_;
    // 静态互斥锁,用于线程同步
    static std::mutex mutex_;
};

// 初始化静态成员变量
Singleton* Singleton::instance_ = nullptr;
std::mutex Singleton::mutex_;

int main() {
    // 获取单例实例并调用示例方法
    Singleton* singleton1 = Singleton::getInstance();
    singleton1->doSomething();

    // 再次获取单例实例(应该是同一个实例)
    Singleton* singleton2 = Singleton::getInstance();
    singleton2->doSomething();

    // 注意:main函数结束时,单例实例的析构函数会被调用
    return 0;
}

3.2.2、组成部分

(1)私有的构造函数
懒汉式单例模式通过将构造函数私有化,防止外部通过new关键字直接创建多个实例。这是实现单例模式的关键一步,因为如果没有私有化构造函数,外部代码就可以随意创建类的实例,从而破坏单例模式的规则。
(2) 私有的静态实例变量
懒汉式单例模式使用一个私有的静态实例变量来存储唯一的实例。这个变量在类加载时不会被初始化,而是在第一次调用获取实例的方法时才会被创建。由于该变量是静态的,因此它只会在类的整个生命周期中存在一个实例。
(3) 公共的静态方法
懒汉式单例模式通过提供一个公共的静态方法来获取唯一的实例。这个方法首先会检查私有的静态实例变量是否为null,如果是,则创建一个新的实例并将其赋值给该变量。如果实例已经存在,则直接返回该实例。
(4)线程安全机制(可选)
在多线程环境下,懒汉式单例模式可能会出现多个线程同时创建实例的情况。为了解决这个问题,可以在获取实例的方法中添加同步机制,如使用synchronized关键字或java.util.concurrent包中的锁机制来确保只有一个线程能够创建实例。

3.2.3、优缺点

(1) 优点:节省内存资源,因为实例只在需要时才创建。
(2) 缺点:非线程安全,因为多个线程可能同时进入创建实例的代码块,导致创建多个实例。需要额外的同步机制来保证线程安全。

3.2.4、应用场景

(1)延迟加载:如果实例的创建开销较大,或者实例的创建和初始化过程会消耗较多资源,那么懒汉模式可以通过延迟加载来节省资源。只有在实际需要使用时,才会创建和初始化实例。
(2)节省内存:在某些情况下,如果实例在类加载时并不需要使用,那么懒汉模式可以避免不必要的内存占用。只有在需要时,才会分配内存和创建实例。
(3)实例创建依赖条件:如果实例的创建依赖于某些条件或参数,并且这些条件或参数在类加载时并不确定,那么懒汉模式可以根据实际情况来创建实例。这提供了更大的灵活性和适应性。

4、面试常问问题

对于C++的单例模式,面试高频问题主要集中在单例模式的基本概念、应用场景、实现方式及其相关细节上。以下是一些可能的面试问题及简要解答:
一、单例模式的基本概念
(1)什么是C++中的单例模式?
单例模式是一种设计模式,它确保一个类只有一个实例,并提供一个全局访问点来访问该实例。在C++中,这通常通过私有化构造函数、拷贝构造函数和赋值运算符,并提供一个静态的公共方法来获取实例来实现。
(2)单例模式的主要目的是什么?
单例模式的主要目的是确保整个进程中,只有一个类的实例,并且提供一个统一的访问接口。这通常用于需要全局访问并共享资源的场景,如日志记录器、配置管理器和数据库连接器等。
二、单例模式的应用场景
(1)请列举一些C++中单例模式的应用场景?
日志记录器:确保整个应用程序中只有一个日志记录器实例,以便集中管理日志输出。
配置管理器:用于读取和管理应用程序的配置信息,确保配置信息的一致性和全局可访问性。
数据库连接器:在需要连接数据库时,确保只有一个数据库连接器实例,以减少资源消耗和提高性能。
三、单例模式的实现方式
(1)C++中实现单例模式有哪些常见的方法?
饿汉式:在类加载时就初始化实例,线程安全但可能会浪费空间。
懒汉式:在第一次调用时才初始化实例,实现了懒加载,但需要注意线程安全问题。可以通过加锁或使用双重检查锁来确保线程安全。
静态内部类实现:利用C++11中的局部静态变量特性,在第一次调用时初始化实例,线程安全且效率高。
(2)什么是双重检查锁(DCL)?它在C++单例模式中的作用是什么?
双重检查锁是一种优化技术,用于在懒汉式单例模式中确保线程安全的同时提高效率。它首先检查实例是否已经存在,如果不存在则加锁,然后再次检查实例是否存在(以避免在加锁期间其他线程已经创建了实例),最后创建实例。这样可以减少不必要的加锁操作,提高性能。
(3)为什么C++11中的局部静态变量可以用于实现线程安全的单例模式?
C++11标准保证了局部静态变量的初始化是线程安全的。因此,在静态方法中利用局部静态变量来初始化单例实例,可以确保在多线程环境中只有一个线程能够创建实例,而其他线程将直接获取到已经创建的实例。
四、单例模式的进阶问题
(1)如何防止C++中的单例模式被反射攻击?
在C++中,由于不存在像Java那样的反射机制,因此通常不需要特别考虑防止反射攻击的问题。但是,如果使用了某些支持反射的库或框架,则需要通过适当的措施来防止反射攻击,如私有化构造函数和析构函数等。
(2)如何确保C++中的单例模式在序列化和反序列化过程中保持唯一性?
在C++中,如果单例类需要被序列化,则需要实现自定义的序列化逻辑。在反序列化过程中,可以检查是否已经存在实例,如果存在则直接返回该实例而不是创建新实例。这通常需要在序列化时保存一些额外的信息来标识实例的唯一性。
(3)C++中的单例模式是否可以被删除或重置?
在大多数情况下,单例模式不需要被删除或重置。因为单例实例通常在整个应用程序的生命周期内都存在,并且被多个模块或组件共享。然而,在某些特殊情况下(如单元测试或应用程序重置等),可能需要提供一种机制来删除或重置单例实例。这可以通过在单例类中添加一个专用的销毁函数来实现,但需要注意线程安全和资源释放等问题。

5、总结

单例模式是一种简单而有效的设计模式,但在使用时需要注意其优缺点和注意事项,以确保其正确性和高效性。

6、参考文章

本文章由文心一言搜索的来的。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

uniapp中使用ruoyiPlus中的加密使用(crypto-js)

package.json中添加 "crypto-js": "^4.2.0", "jsencrypt": "^3.3.2",但是vue2中使用 import CryptoJS from cryptojs; 这一步就会报错 参照 参照这里&#xff1a;vue2使用CryptoJS实现信息加解密 根目录下的js文档中新增一个AESwork.…

【SQL Server】教材数据库(1)

1 利用sql建立教材数据库&#xff0c;并定义以下基本表&#xff1a; 学生&#xff08;学号&#xff0c;年龄&#xff0c;性别&#xff0c;系名&#xff09; 教材&#xff08;编号&#xff0c;书名&#xff0c;出版社编号&#xff0c;价格&#xff09; 订购&#xff08;学号…

全国计算机设计大赛大数据主题赛(和鲸赛道)经验分享

全国计算机设计大赛大数据主题赛&#xff08;和鲸赛道&#xff09;经验分享 这是“和鲸杯”辽宁省普通高等学校本科大学生计算机设计竞赛启动会汇报—大数据主题赛的文档总结。想要参加2025年此比赛的可以借鉴。 一、关于我 人工智能专业 计赛相关奖项&#xff1a; 2022年计…

AI对接之JSON Output

AI的JSON Output 实际对接指南 前言 本系列AI的API对接均以 DeepSeek 为例&#xff0c;其他大模型的对接方式类似。 在现代软件开发中&#xff0c;JSON&#xff08;JavaScript Object Notation&#xff09;作为一种轻量级的数据交换格式&#xff0c;因其简洁和易于人阅读的特…

Vue3实现PDF在线预览功能

​&#x1f308;个人主页&#xff1a;前端青山 &#x1f525;系列专栏&#xff1a;Vue篇 &#x1f516;人终将被年少不可得之物困其一生 依旧青山,本期给大家带来Vue篇专栏内容:Vue3现PDF在线预览功能 前言 在开发中&#xff0c;PDF预览和交互功能是一个常见的需求。无论是管理…

SpringBootWeb案例-1

文章目录 SpringBootWeb案例1. 准备工作1.1 需求&环境搭建1.1.1 需求说明1.1.2 环境搭建 1.2 开发规范 2. 部门管理2.1 查询部门2.1.1 原型和需求2.1.2 接口文档2.1.3 思路分析2.1.4 功能开发2.1.5 功能测试 2.2 前后端联调2.3 删除部门2.3.1 需求2.3.2 接口文档2.3.3 思路…

css实现垂直文本

效果 知识 writing-mode: <value>; 可选值 horizontal-tb: 默认值。文本从左到右&#xff08;或从右到左&#xff09;排列&#xff0c;然后从上到下。vertical-rl: 文本从上到下排列&#xff0c;然后从右到左。适用于垂直书写的方向&#xff0c;如日语和中文。vertica…

vim里搜索关键字

vim是linux文本编辑器的命令&#xff0c;再vi的基础上做了功能增强 使用方法如下 1. / 关键字, 回车即可, 按n键查找关键字下一个位置 2.? 关键字, 回车即可, 按n键查找关键字下一个位置 3.示例

Qt之QtConcurrent

简介 QtConcurrent是针对qt中多线程相关的高层封装&#xff0c;如QFuture 结构 Qtconcurrent命名空间中的run支持的有 其对应的functor下结构为 类关系 functor对应的类核心关系为 #mermaid-svg-KLxZquz9yRsiYvQL {font-family:"trebuchet ms",verdana,a…

鸿蒙应用开发搬砖经验之-ArkWeb加载页面的超简单示例

前言 系统环境&#xff1a;Mac mini M2 14.5 (23F79) 开发IDE&#xff1a;DevEco Studio 5.0.1 Release 示例 第一步&#xff1a;创建一个Empty Ability工程 第二步&#xff1a;先run一下&#xff0c;确定工程初步化正常&#xff0c;模拟器正常启动应用&#xff08;要先提…

大模型系列17-RAGFlow搭建本地知识库

大模型系列17-RAGFlow搭建本地知识库 安装ollama安装open-wehui安装并运行ragflowRAG&#xff08;检索、增强、生成&#xff09;RAG是什么RAG三过程RAG问答系统构建步骤向量库构建检索模块生成模块 RAG解决LLM的痛点 使用ragflow访问ragflow配置ollama模型添加Embedding模型添加…

SimForge HSF 案例分享|复杂仿真应用定制——UAVSim无人机仿真APP(技术篇)

导读 「神工坊」核心技术——「SimForge HSF高性能数值模拟引擎」支持工程计算应用的快速开发、自动并行&#xff0c;以及多域耦合、AI求解加速&#xff0c;目前已实现航发整机数值模拟等多个系统级高保真数值模拟应用落地&#xff0c;支持10亿阶、100w核心量级的高效求解。其低…

微电网到底是什么?和光伏有什么关系?

在现代能源体系中&#xff0c;微电网作为一种新型的电力系统结构&#xff0c;正逐渐受到广泛关注和应用。那么&#xff0c;微电网到底是什么&#xff1f;它与光伏又有怎样的关系呢&#xff1f;本文将对此进行详细解析。 微电网的基本概念 微电网&#xff08;Micro-Grid&#x…

印象笔记06——再谈谈更新

印象笔记06——再谈谈更新 [!CAUTION] 好吧&#xff0c;我承认在前五期的努力下&#xff0c;我还是用的obsidian多一些。印象笔记很大程度用来弄清单&#xff0c;但是扭头看了看自己的会员时间&#xff0c;不能浪费啊&#xff01;本期再谈谈印象笔记近期的一些更新&#xff0c;…

爱死机第四季(秘密关卡)4KHDR国语字幕

通过网盘分享的文件&#xff1a;love_death_robot 链接: https://pan.baidu.com/s/1bG3Xtdopenil2O_y93hY_g?pwd8kib 提取码: 8kib

Android Studio学习笔记

01-课程前面的话 02-Android 发展历程 03-Android 开发机器配置要求 04-Android Studio与SDK下载安装 05-创建工程与创建模拟器

unity学习3:如何从github下载开源的unity项目

目录 1 网上别人提供的一些github的unity项目 2 如何下载github上的开源项目呢&#xff1f; 2.1.0 下载工具 2.1.1 下载方法1 2.1.2 下载方法2&#xff08;适合内部项目&#xff09; 2.1.3 第1个项目 和第4项目 的比较 第1个项目 第2个项目 第3个项目 2.1.4 下载方法…

自动驾驶新纪元:城区NOA功能如何成为智能驾驶技术的分水岭

目录 一、NOA 的定义 二、NOA 的主要特点 导航集成 场景覆盖 智能决策 高级感知能力 驾驶员参与 三、NOA 的优势 四、NOA的衡量指标 定性评价指标 安全性评价指标定义 可靠性评价指标定义 舒适性评价指标定义 通行效率评价指标 定量评价指标 五、代表厂商的实测…

解决uniapp H5页面限制输入框只能输数字问题

工作记录 最最近在做 uniapp 开发的移动端 H5 页面&#xff0c;有个需求是金额输入框只能输入数字&#xff0c;不能输入小数点和其他字符&#xff0c;经过各种尝试&#xff0c;发现其他字符可以通过正则过滤掉&#xff0c;但是输入小数点的话&#xff0c;因为没有触发 input 和…

JDK8源码分析Jdk动态代理底层原理

本文侧重分析JDK8中jdk动态代理的源码&#xff0c;若是想看JDK17源码分析可以看我的这一篇文章 JDK17源码分析Jdk动态代理底层原理-CSDN博客 两者之间有着略微的差别&#xff0c;JDK17在JDK8上改进了不少 目录 源码分析 过程 生成的代理类大致结构 本文侧重分析JDK8中jdk…