『 C++ 』单例模式与IO流

news2024/9/20 10:38:41

文章目录

    • 单例模式
      • 饿汉加载的单例模式实现
      • 懒汉加载的单例模式实现
    • IO流
      • 类型之间的转换


单例模式

单例模式是一种创建型设计模式;

确保一个类在应用程序的生命周期内仅有一个实例并提供一个全局访问点来访问该实例;

单例模式主要目的是为了控制某些类的实例化以避免产生多个实例,从而节省资源和避免数据不一致问题;

单例模式的核心要点为如下:

  • 唯一性

    单例模式确保一个类只有一个实例,意味着该类的所有对象共享相同的状态和行为;

  • 全局访问点

    单例模式提供了一个静态方法(getInstance()),使得客户端可通过这个方法访问唯一的实例而不需要创建对象;

  • 加载方式

    单例模式可通过需求来自定义需要的加载方式,常见的加载方式为饿汉加载懒汉加载;

    • 饿汉加载

      饿汉加载指的是单例模式在进程创建时就对单例的资源进行初始化,从而间接提高运行的速度;

      因为进程启动时单例就已经被初始化意味着不需要再花时间对该单例进行初始化操作;

      相同的由于初始化的时机是在进程启动时,所以饿汉加载方式的启动速度要较慢;

      饿汉加载是线程安全的,但饿汉模式加载处的实例若是没有被使用则是一种空间的浪费的行为;

      尽管饿汉加载是线程安全的,也只是代表在加载过程中是安全的,若是实例中存在可能出现资源竞争的临界资源时同样必须为该单例考虑同步互斥问题;

    • 懒汉加载

      懒汉加载是单例的一种加载模式,懒汉加载模式旨在需要时对实例进初始化加载,从而提高进程的加载速度;

      由于懒汉加载模式是在需要时对实例进行加载,这意味着不需要花费时间在进程启动时对实例进行资源的加载;

      与饿汉模式不同,懒汉加载不是线程安全的,懒汉加载模式涉及到当需要该实例时多个线程同时调用加载函数对实例进行初始化加载,故在设计懒汉加载时需要考虑多线程并发情况下线程的同步与互斥问题;

      与饿汉模式不同的是懒汉模式不存在"一定要加载,不一定使用"的问题所引发的资源浪费的可能;

单例模式的实现步骤一般为:

  • 私有化构造函数

    通过私有化构造函数防止类外代码随意对实例进行控制从而可能产生多个实例;

  • 静态私有成员变量

    在类中声名一个静态的私有成员变量,用于存储该类的唯一实例,这个成员变量可以是一个对象也可以是一个指针变量,取决于加载方式;

  • 静态公有方法

    在单例模式中会提供一个静态共有方法(通常命名为getInstance())来获取该类的唯一实例;


饿汉加载的单例模式实现

// 单例类的定义
class SingletonInstance {
public:
    // 静态方法,用于获取唯一实例
    static SingletonInstance& getInstance() {
        return instance_; // 返回静态实例
    }

    // 打印示例方法
    void Print() {
        std::cout << "This is a Singleton model" << std::endl;
    }

private:
    // 私有构造函数,防止外部实例化
    SingletonInstance() {
        std::cout << "SingletonInstance()" << std::endl;
    }

    // 私有析构函数,防止外部删除实例
    ~SingletonInstance() {
        std::cout << "~SingletonInstance()" << std::endl;
    }

    // 删除拷贝构造函数,防止复制
    SingletonInstance(const SingletonInstance&) = delete;

    // 删除赋值操作符,防止赋值
    SingletonInstance& operator=(const SingletonInstance&) = delete;

    // 静态成员变量,存储唯一实例
    static SingletonInstance instance_;
};

// 静态成员变量初始化
SingletonInstance SingletonInstance::instance_;

int main() {
    sleep(3); // 延迟3秒
    // 获取单例实例并调用打印方法
    SingletonInstance::getInstance().Print();
    return 0;
}

在这个单例模式中定义了一个单例类,通过静态成员变量instance_存储了唯一实例,该类内私有成员将在类外进行定义;

getInstance()为一个静态方法,用于返回类的唯一实例,由于为一个静态方法,其不隐含this指针,但其有权访问该类中的所有成员;

私有化构造函数以确保类外部无法创建或销毁单例实例从而保持单例模式的完整性;

通过删除拷贝构造和赋值重载防止通过赋值或拷贝的方式创建新的实例从而进一步保证单例模式的唯一性;


懒汉加载的单例模式实现

class SingletonInstanceLazy {
 public:
  // 获取单例实例的静态方法
  static SingletonInstanceLazy* getInstance() {
    if (!instance_) { // 第一次检查实例是否为空
      lock_.lock(); // 加锁,确保线程安全
      if (!instance_) { // 再次检查实例是否为空,双重检查锁定
        instance_ = new SingletonInstanceLazy(); // 创建单例实例
      }
      lock_.unlock(); // 解锁
    }
    return instance_; // 返回单例实例
  }

 protected:
  // 构造函数和析构函数被保护以防止外部创建和销毁实例
  SingletonInstanceLazy() { cout << "SingletonInstanceLazy()" << endl; }
  ~SingletonInstanceLazy() { cout << "~SingletonInstanceLazy()" << endl; }

  // 禁止拷贝构造和赋值操作以防止生成多个实例
  SingletonInstanceLazy(const SingletonInstanceLazy&) = delete;
  SingletonInstanceLazy& operator=(const SingletonInstanceLazy&) = delete;

  // 嵌套类,用于程序退出时释放单例实例
  class Gc {
   public:
    ~Gc() {
      delete instance_; // 删除单例实例
      instance_ = nullptr; // 将指针置为空
    }
  };

 private:
  static SingletonInstanceLazy* instance_; // 存储唯一的单例实例指针
  static mutex lock_; // 用于保护访问单例实例的互斥锁
  static Gc gc_; // 静态嵌套类实例,用于自动回收单例实例资源
};

// 初始化静态成员变量
SingletonInstanceLazy* SingletonInstanceLazy::instance_ = nullptr;
mutex SingletonInstanceLazy::lock_;

这种模式在第一次调用getInstance()时才会创建实例而不是在程序启动时创建;

通过延迟实例化节省内存,同时加快进程加载速度;

  • 线程安全性

    在单例模式中确保实例化时的线程安全是关键;

    使用mutex互斥锁来保护instance_初始化操作,防止多个线程同时创建多个实例;

  • 双重检查锁定

    getInstance()使用了双重检查锁定机制,首先检查instance_是否为空,如果为空则进入锁定区;

    在锁定区内再次检查instance_是否为空以确保只有一个线程能够成功创建实例;

  • 禁止拷贝和赋值

    构造函数和析构函数被protected访问限定符所给保护;

    拷贝构造函数和赋值重载函数被删除,以防止拷贝构造或赋值重载产生多个实例;

  • 资源自动释放

    内部类Gc用于在成熟结束时自动删除单例实例,通过静态成员变量gc_的析构函数实现;

    当程序结束时,Gc的析构函数会被调用,从而释放SingletonInstanceLazy实例;

一般new的懒汉对象不需要释放,但可能需要进行其他操作例如数据持久化(需要写到文件中),可通过定义的Gc的析构函数来进行实现;


IO流

是一种抽象概念,表示数据的有序传输;

流可以从数据源读入数据或将数据写入到数据目标,C++中的流可以分为两类:

  • 输入流

    用于从数据源读取数据;

  • 输出流

    用于将数据写入到数据目标;

C++的IP流系统基于类的层次结构,可以分为以下几类:

  • 基本流类:

    • istream

      基于输入流类,提供了从流中读取数据的基本功能;

    • ostream

      基于输出流类,提供了向流中写入数据的基本功能;

    • iostream

      继承自istreamostream,提供了输入和输出的双向功能;

    其中基本流类都继承自ios类,形成了一个棱形继承,采用虚继承的方式使得iostream具有输入和输出的双向功能;

  • 文件流类

    • ifstream

      继承自istream,用于从文件中读取数据;

    • ofstream

      继承自ostream,用于向文件中写入数据;

    • fstream

      继承自iostream,用于对文件进行输入输出操作;

  • 字符串流类

    • istringstream

      继承自istream,用于从字符串读取数据;

    • ostringstream

      继承自istream,用于向字符串写入数据;

    • stringstream

      继承自iostream,用于对字符串进行输入和输出操作;


类型之间的转换

  • 内置类型转内置类型

    内置类型之间,相近类型可以进行转换,转换的方式一般通过隐式类型转换或是强制类型转换;

    int main() {
      double a = 10.03;
      int b = a;  // 相近类型隐式类型转换
      cout << a << " : " << b << endl;
      return 0;
    }
    
    /*
    	运行结果为:
    	$ ./mytest 
        10.03 : 10
    */
    
  • 自定义类型转自定义类型

    自定义类型转自定义类型通过构造函数进行转换;

    class A {
     public:
      A() {}
      ~A() {}
    
     private:
    };
    
    class B {
     public:
      B(A& a) {}
      ~B() {}
    
     private:
    };
    
    int main() {
      A a;
      B b = a; // 通过构造函数完成自定义类型之间的转换
      return 0;
    }
    
  • 内置类型转自定义类型

    内置类型转自定义类型同样采用构造函数;

    class B {
     public:
      B(int a) {}
      ~B() {}
    
     private:
    };
    
    int main() {
      int a = 10;
      B b = a;  // 通过构造函数完成内置类型转自定义类型之间的转换
      return 0;
    }
    
  • 自定义类型转内置类型

    自定义类型转内置类型可通过operator typename()进行转换;

    class B {
     public:
      operator int() { return 10; }
    };
    
    int main() {
      B b;
      int i = b;
      cout << i << endl;
      return 0;
    }
    /*
    	运行结果为:
    	$ ./mytest 
        10	
    */
    

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

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

相关文章

Java面试--框架--Spring MVC

Spring MVC 目录 Spring MVC1.spring mvc简介2.spring mvc实现原理2.1核心组件2.2工作流程 3.RESTful 风格4.拦截器4.1过滤器、监听器、拦截器的对比4.2 拦截器基本概念4.3 拦截器的实现 1.spring mvc简介 Spring MVC是一款由Spring Framework 提供的 web组件&#xff0c;实现…

MySQL-MVCC举例说明

在数据库系统中&#xff0c;多版本并发控制&#xff08;MVCC, Multi-Version Concurrency Control&#xff09; 是一种用于提高并发性能的机制&#xff0c;它允许多个事务同时读取和写入数据&#xff0c;而不会产生锁等待和阻塞的问题。MySQL 的 InnoDB 存储引擎广泛使用了 MVC…

C#MVC返回DataTable到前端展示。

很久没写博客了&#xff0c;闭关太久&#xff0c;失踪人口回归&#xff0c;给诸位道友整点绝活。 交代下背景&#xff1a;要做一个行转列的汇总统计&#xff0c;而且&#xff0c;由于是行转列&#xff0c;列的数量不固定&#xff0c;所以&#xff0c;没法使用正常的SqlSugar框…

C++入门基础知识13

C 的关键字&#xff08;接上一篇博文&#xff09;&#xff01;&#xff01; 10. const_cast用法&#xff1a; 该运算符用来修改类型的 const 或 volatile 属性。除了 const 或 volatile 修饰之外&#xff0c; type_id 和 expression 的类型是一样的。常量指针被转化成非常量指针…

催收业务怎么提高接通率

提高催收呼叫业务的接通率是一个综合性的任务&#xff0c;需要从多个方面进行优化。以下是一些具体的策略和建议&#xff1a; 一、优化呼叫时间与频次 1. 选择合适的呼叫时间&#xff1a;通过分析目标客户的活跃时段&#xff0c;选择他们最可能接听电话的时间进行呼叫…

iOS Object-C 创建类别(Category) 与使用

有时候使用系统给出类或者第三方的类,但是呢它们自带的属性和方法又太少,不够我们的业务使用,这时候就需要给“系统的类或者第三方类”创建一个类别(Category),把自己的想添加的属性和方法写进来. Category模式用于向已经存在的类添加方法从而达到扩展已有类的目的 一:创建Ca…

零碳工厂:我国工业转型升级的绿色引擎

面对全球气候变化的严峻挑战&#xff0c;我国提出了碳达峰和碳中和的宏伟目标。零碳工厂作为工业领域实现这一目标的重要途径&#xff0c;正成为推动我国工业转型升级的绿色引擎。本文将提供一站式零碳工厂服务指南&#xff0c;帮助企业迈向零碳排放&#xff0c;共同构建绿色低…

零售企业做好人事管理并不难!智能化人事管理平台解决企业三大痛点!

在零售行业的激烈竞争中&#xff0c;优秀的人事管理策略是企业成功的关键。然而&#xff0c;传统的人事管理模式常常面临人员分散、流程繁琐、跨部门协作困难等挑战。为了应对这些问题&#xff0c;一站式人事管理平台的出现&#xff0c;为企业提供了数字化的解决方案。本文将探…

[论文泛读]zkLLM: Zero Knowledge Proofs for Large Language models

文章目录 介绍实验数据实验数据1实验数据2实验数据3 介绍 这篇文章发在CCS2024&#xff0c;CCS是密码学领域的顶会。作者是来自加拿大的University of Waterloo。文章对大语言模型像GPT和LLM等大语言模型实现了零知识可验证执行&#xff0c;但不涉及零知识可验证训练。个人觉得…

PAT--1124.最近的斐波那契数

题目描述 算法分析 找到把n夹在中间的两个斐波那契数列的数字&#xff0c;比如输入的数字9&#xff0c;你需要找到数字8和13&#xff0c;然后再输出差值绝对值较小的那个数字&#xff0c;如果一样&#xff0c;输出较小的数字 完整代码 #include<iostream> using names…

npm install pnpm -g 报错的解决方法

npm install pnpm -g 报错的解决方法 npm error code ETIMEDOUT npm error errno ETIMEDOUT npm error network request to https://registry.npmjs.org/pnpm failed, reason: npm error network This is a problem related to network connectivity. npm error network In mo…

三防加固平板电脑定制:现代工业环境的最佳选择

在数字化转型的浪潮中&#xff0c;工业领域正经历着前所未有的变革。智能设备的引入&#xff0c;尤其是定制化的三防加固平板电脑&#xff0c;正在成为现代工业环境中的关键工具。这些设备不仅能够承受严苛的工作条件&#xff0c;还能提供高度定制化的功能&#xff0c;以满足特…

萌啦数据使用体验,萌啦数据ozon体验如何

在跨境电商的浩瀚星海中&#xff0c;Ozon作为俄罗斯及独联体地区领先的电商平台&#xff0c;正以其独特的魅力和无限潜力吸引着全球卖家的目光。而在这片蓝海中航行&#xff0c;精准的数据分析与洞察无疑是每位船长手中的罗盘。今天&#xff0c;我们就来深入探讨一款备受好评的…

刷题技巧:双指针法的核心思想总结+例题整合+力扣接雨水双指针c++实现

双指针法的核心思想是通过同时操作两个指针来遍历数据结构&#xff0c;通常是数组或链表&#xff0c;以达到优化算法性能的目的。具体来说&#xff0c;双指针法能够减少时间复杂度、空间复杂度&#xff0c;或者简化逻辑结构。以下是双指针法的几个核心思想&#xff1a; ps 下面…

rem、em 和 px、inherit 加案例

一、rem、em 和 px 是三种常用的 CSS 长度单位&#xff0c;每种单位在不同的场景下有不同的应用和效果。以下是它们的区别&#xff1a; 以下是它们的区别&#xff1a; px (像素) 定义: px 是相对单位&#xff0c;表示屏幕上的一个物理像素点。它是一个固定的单位&#xff0c;…

手机电量消耗分析工具 Battery Historian 指南

阅读五分钟&#xff0c;每日十点&#xff0c;和您一起终身学习&#xff0c;这里是程序员Android 本篇文章主要介绍 Android 开发中 电量 的部分知识点&#xff0c;通过阅读本篇文章&#xff0c;您将收获以下内容: 一、安装Battery Historian二、收集Batterystats 数据三、使用B…

YOLO好像也没那么难?

“学YOLO的念头是想整个游戏外挂&#xff01;” 目录 基本原理 模型推理 IOU交并比 NMS非极大值抑制 模型训练 损失函数LOSS 代码实现 YOLO学习渠道 基本原理 模型推理 学习一个新的神经网络结构&#xff0c;作者认为整明白输入和输出是怎么回事就OK了&#xff0c;至于…

平安城市/雪亮工程现状及需求分析:EasyCVR视频汇聚平台助力雪亮工程项目建设

一、背景现状 经过近几年的努力&#xff0c;平安城市雪亮工程建设取得了显著的成绩&#xff0c;完成了前端高清视频点位和高清卡口系统建设&#xff0c;建成了&#xff08;视频监控类&#xff09;、&#xff08;卡口类&#xff09;和&#xff08;应用类&#xff09;的平台。这…

Linux笔记 --- 目录检索

基本概念 Linux中的目录与windows的文件夹相似但是概念大相径庭&#xff0c;windows中子文件一定不会比母文件夹大&#xff0c;但在Linux目录中是可以实现的&#xff0c;目录是一种文件索引表&#xff0c;下图是分区和目录的关系 Linux中目录是一组由文件名和索引号组成的索引表…

JavaScript基础(33)_鼠标滚轮滚动事件、键盘事件

鼠标滚轮滚动事件&#xff1a;onwheel 获取鼠标滚轮滚动的方向&#xff1a;wheelDelta 比如&#xff1a;向上滚动&#xff1a;109 &#xff08;所有正值都是向上&#xff09; 向下滚动&#xff1a;-109&#xff08;所有负值都是向下&#xff09; 注意&#xff1a;当…