享元模式(Flyweight Pattern)

定义

享元模式(Flyweight Pattern)是一种结构型设计模式,其主要目的是通过共享尽可能多的数据来减少内存使用和提高性能。它通常在需要创建大量相似对象的情况下使用,以减少对象的数量和内存消耗。

示例

#include <iostream>
#include <map>// 抽象享元类
class Flyweight {
public:virtual void operation() = 0;
};// 具体享元类
class ConcreteFlyweight : public Flyweight {
private:int intrinsicState; // 内在状态public:ConcreteFlyweight(int intrinsicState) : intrinsicState(intrinsicState) {}void operation() override {std::cout << "Concrete Flyweight with intrinsic state: " << intrinsicState << std::endl;}
};// 享元工厂类
class FlyweightFactory {
private:std::map<int, Flyweight*> flyweights; // 存储享元对象的容器public:// 获取享元对象Flyweight* getFlyweight(int key) {if (flyweights.find(key) == flyweights.end()) {// 如果容器中不存在对应的享元对象,则创建一个新的享元对象并加入容器flyweights[key] = new ConcreteFlyweight(key);}return flyweights[key];}
};int main() {FlyweightFactory factory;// 客户端使用享元对象Flyweight* flyweight1 = factory.getFlyweight(1);flyweight1->operation();Flyweight* flyweight2 = factory.getFlyweight(2);flyweight2->operation();Flyweight* flyweight3 = factory.getFlyweight(1); // 重复使用相同的享元对象flyweight3->operation();// 释放资源delete flyweight1;delete flyweight2;delete flyweight3;return 0;
}

在这个示例中:

  • Flyweight 是享元类,声明了操作的接口。
  • ConcreteFlyweight 是具体享元类,实现了操作接口,并包含了内在状态(intrinsic state)。
  • FlyweightFactory 是享元工厂类,负责创建和管理享元对象,它通过一个容器存储已创建的享元对象。
  • main 函数中,客户端通过 FlyweightFactory 获取享元对象,并使用这些对象进行操作。当客户端请求的对象已经存在时,FlyweightFactory 会返回现有的对象,否则会创建一个新的对象。

通过共享内在状态,享元模式可以大幅减少内存占用。在实际应用中,内在状态通常是不变的,而外在状态可以在运行时修改。

应用示例

在现实生活中,我们可以将享元模式应用于多种场景,其中一个典型的例子是“邮票打印”。

想象一下,在一个邮局或者打印店里,有许多人需要打印邮票。每个邮票的设计都是相同的,只是邮票上的面值(如1元、2元、5元等)可能不同。如果每次打印邮票都创建一个新的对象,将会浪费大量的内存和计算资源。

为了解决这个问题,我们可以采用享元模式。我们可以创建一个“邮票”类,这个类包含了邮票的通用属性和方法。然后,我们可以创建一个“邮票工厂”类,用于生成和管理邮票对象。

在邮票工厂中,我们可以使用一个哈希表(或其他数据结构)来存储已经创建的邮票对象。当有人需要打印邮票时,邮票工厂首先检查哈希表中是否已经存在具有相同面值的邮票对象。如果存在,则直接返回该对象;如果不存在,则创建一个新的邮票对象并将其添加到哈希表中。

通过这种方式,我们可以避免为每个面值都创建一个新的邮票对象,从而节省了内存和计算资源。同时,由于邮票对象是被共享的,因此也提高了系统的性能。

下面是一个简化的代码示例,展示了如何在C++中实现享元模式:

#include <iostream>  
#include <unordered_map>  
#include <string>  // 邮票类  
class Stamp {  
public:  Stamp(const std::string& value) : value_(value) {}  void print() {  std::cout << "Printing stamp with value: " << value_ << std::endl;  }  private:  std::string value_; // 邮票面值  
};  // 邮票工厂类  
class StampFactory {  
public:  // 获取指定面值的邮票对象  Stamp* getStamp(const std::string& value) {  // 检查缓存中是否存在该面值的邮票对象  if (stamps_.find(value) != stamps_.end()) {  return stamps_[value]; // 返回已存在的对象  }  // 如果不存在,则创建一个新的邮票对象并添加到缓存中  Stamp* newStamp = new Stamp(value);  stamps_[value] = new Stamp;  return new Stamp;  }  private:  std::unordered_map<std::string, Stamp*> stamps_; // 缓存邮票对象  
};  int main() {  StampFactory factory;  // 打印面值为1元的邮票  Stamp* stamp1 = factory.getStamp("1元");  stamp1->print();  // 打印面值为2元的邮票  Stamp* stamp2 = factory.getStamp("2元");  stamp2->print();  // 再次打印面值为1元的邮票(此时应该返回之前创建的对象)  Stamp* stamp3 = factory.getStamp("1元");  stamp3->print();  // 释放内存(在实际应用中,可能需要更智能的内存管理策略)  delete stamp1;  delete stamp2;  delete stamp3;  return 0;  
}

首先,我们注意到在 StampFactory 类中,我们使用 std::unordered_map 来缓存已经创建的邮票对象。每次调用 getStamp 方法时,我们首先检查这个哈希表中是否已经有相同面值的邮票对象。如果有,我们返回该对象的指针;如果没有,我们创建一个新的对象并将其添加到哈希表中。

然而,上述代码示例在内存管理方面存在一些问题。由于我们直接使用了 new 关键字来创建邮票对象,并在 main 函数的末尾使用 delete 关键字来释放内存,这可能会导致内存泄漏。在实际应用中,我们需要一种更智能的内存管理策略来确保所有对象在使用完毕后都能被正确地释放。

一种常见的解决方案是使用智能指针(如 std::shared_ptrstd::unique_ptr)来管理对象的生命周期。智能指针可以自动释放不再需要的对象,从而避免内存泄漏。

此外,我们还需要考虑线程安全的问题。如果多个线程同时调用 StampFactorygetStamp 方法,可能会导致竞态条件(race condition)。为了解决这个问题,我们可以使用互斥锁(如 std::mutex)来确保每次只有一个线程能够访问哈希表。

最后,值得注意的是,虽然享元模式可以减少对象的数量和内存占用,但它并不总是最佳选择。在某些情况下,创建和销毁对象的开销可能并不大,或者每个对象的状态差异很大,导致共享对象变得不切实际。在这些情况下,使用享元模式可能会带来不必要的复杂性。

综上所述,享元模式是一种用于减少对象数量和提高性能的有效策略,但它在实现和使用过程中需要考虑多种因素,包括内存管理、线程安全和适用场景等。

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

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

相关文章

扫雷2(2537)

题目描述 扫雷游戏是一款十分经典的单机小游戏。它的精髓在于&#xff0c;通过已翻开格子所提示的周围格地雷数&#xff0c;来判断未翻开格子里是否是地雷。现在给出 m 行 n 列的雷区中的地雷分布&#xff0c;要求计算出每个非地雷格的周围格地雷数。 注&#xff1a;每个格子周…

docker build基本命令

背景 我们经常会构建属于我们应用自己的镜像&#xff0c;这种情况下编写dockerfile文件不可避免&#xff0c;本文就来看一下常用的dockerfile的指令 常用的dockerfile的指令 首先我们看一下docker build的执行过程 ENV指令&#xff1a; env指令用于设置shell的环境变量&am…

1019: 【C1】【循环】【for】满足条件的数累加

题目描述 将正整数 m 和 n 之间&#xff08;包括 m 和 n&#xff09;能被 17 整除的数累加。其中&#xff0c;0 < m < n < 1000。 输入 一行&#xff0c;包含两个整数m和n&#xff0c;其间&#xff0c;以一个空格间隔 输出 输出一行&#xff0c;包行一个整数&…

【c语言】字符函数和字符串函数(下)

前言 书接上回 【c语言】字符函数和字符串函数(上) 上一篇讲解的strcpy、strcat、strcmp函数的字符串长度是不受限制的 而本篇strncpy、strncat、strcnmp函数的字符串长度是受限制的 欢迎关注个人主页&#xff1a;逸狼 创造不易&#xff0c;可以点点赞吗~ 如有错误&#xff0c;…

JANGOW: 1.0.1

kali:192.168.223.128 主机发现 nmap -sP 192.168.223.0/24 端口扫描 nmap -p- 192.168.223.154 开启了21 80端口 web看一下&#xff0c;有个busque.php参数是buscar,但是不知道输入什么&#xff0c;尝试文件包含失败 扫描目录 dirsearch -u http://192.168.223.154 dirse…

Python的字符串操作你用对了吗?

1.创建字符串 字符串指的是使用一对单引号或者一对双引号或者一对三个单引号或者一对三个双引号包裹的文本 # 创建字符串str1 hellostr2 "world"print(str1)print(str2) # 使文本原样输出str3 床上明月光&#xff0c;疑是地上霜。str4 """举头望…

零基础韩语怎么学,柯桥韩语培训留学培训

应用”ㅗ”的单词为&#xff1a; 辅音ㄱ 发音时&#xff0c;将舌后部分向上抬起&#xff0c;舌根接触软腭堵住气流&#xff0c;然后放开使气流冲出而成音。它跟汉语中的”g” 歌(ge)古(gu) 国(guo)中的声母比较相似。 应用”ㄱ”的单词为&#xff1a; &#xff08;1&#x…

10.vue学习笔记(组件数据传递-props回调函数子传父+透传Attributes+插槽slot)

文章目录 1.组件数据传递2.透传Attributes&#xff08;了解&#xff09;禁用Attributes继承 3.插槽slot3.1.插槽作用域3.2.默认内容3.3.具名插槽3.4.插槽中的数据传递3.5.具名插槽传递数据 1.组件数据传递 我们之前讲解过了组件之间的数据传递&#xff0c;props 和 自定义事件…

算法【线性表的查找-顺序查找】

线性表的查找-顺序查找 顺序查找基本思想应用范围顺序表的表示数据元素类型定义查找算法示例分析 时间效率分析顺序查找的特点如何提高查找效率 顺序查找 基本思想 在表的多种结构定义方式中&#xff0c;线性表是最简单的一种。而顺序查找是线性表查找中最简单的一种。 顺序查…

Superhuman 邮箱的替代方案是什么?

Superhuman是一个极好的人工智能工具在电子邮件助理领域。根据SimilarWeb的最新统计&#xff0c;它在全球网站排名中排名第21980位&#xff0c;月访问量为1751798。然而市场上还有许多其他优秀的选择。为了帮助您找到最适合您需求的解决方案&#xff0c;我们为您精心挑选了10种…

Python进阶学习:json.dumps()和json.dump()的区别

Python进阶学习&#xff1a;json.dumps()和json.dump()的区别 &#x1f308; 个人主页&#xff1a;高斯小哥 &#x1f525; 高质量专栏&#xff1a;Matplotlib之旅&#xff1a;零基础精通数据可视化、Python基础【高质量合集】、PyTorch零基础入门教程 &#x1f448; 希望得到您…

Golang基础面试题1

来源于fullstack&#xff0c;绿色为重点 1. Go语言是什么 Go 是一种通用编程语言&#xff0c;设计初衷是为了进行系统编程。它最初是由 Google 的 Robert Griesemer、Rob Pike 和 Ken Thompson 在 2007 年开发的。Go 语言是强类型且静态类型的&#xff0c;它内置了对垃圾回收…

在Ubuntu上为ARM 8处理器安装Python 3.10.4虚拟环境指南

在Ubuntu上为ARM 8处理器安装Python 3.10.4虚拟环境指南 安装Anaconda或Miniconda&#xff1a; 首先&#xff0c;您需要从官方网站下载适用于ARM架构的Anaconda或Miniconda安装包。下载完成后&#xff0c;在终端中使用bash Anaconda3-2019.10-Linux-armv8.sh&#xff08;文件…

【JVM】StringTable 字符串常量池

参考&#xff1a;javaGuide 字符串常量池 是 JVM 为了提升性能和减少内存消耗针对字符串&#xff08;String 类&#xff09;专门开辟的一块区域&#xff0c;主要目的是为了避免字符串的重复创建 String的不可变性 1.通过字面量的方式&#xff08;区别于new&#xff09;给一个…

利用小红书笔记详情API:轻松获取海量笔记内容

小红书是一个非常流行的社交分享平台&#xff0c;特别是深受年轻人喜欢&#xff0c;因为它提供了用户分享生活方式、时尚、美妆、旅行、美食等内容的功能。如果你希望从小红书上获取大量的笔记内容&#xff0c;那么利用小红书的API是一个很好的选择。下面&#xff0c;我将为你介…

图像复原天花板!IR开创性新作实现最佳视觉质量,修复更智能、更逼真

图像复原&#xff08;IR&#xff09;指在已知图像退化的原因和模型的情况下&#xff0c;通过一系列的逆过程来恢复出原始图像的过程。这是一个长期的低级视觉任务&#xff0c;也是图像处理领域的一个重要课题。 随着深度学习技术的发展&#xff0c;图像复原领域不断出现新的网…

容器库(13)-std::unordered_multimap

unordered_multimap是含有键值对的无序关联容器&#xff0c;搜索、移除和插入操作是平均常数的时间复杂度。unordered_multimap在内部没有按任何顺序排列&#xff0c;而是放在桶当中的&#xff0c;放进哪个桶是通过计算key的hash值来决定的。和unordered_map不同的是&#xff0…

Python算法题集_全排列

Python算法题集_全排列 题46&#xff1a;全排列1. 示例说明2. 题目解析- 题意分解- 优化思路- 测量工具 3. 代码展开1) 标准求解【标记数组递归】2) 改进版一【指针递归】3) 改进版二【高效迭代模块】4) 改进版三【高效迭代模块极简代码】 4. 最优算法5. 相关资源 本文为Python…

《剑指 Offer》专项突破版 - 面试题 59、60 和 61 : 详解堆的应用(C++ 实现)

目录 前言 面试题 59 : 数据流的第 k 大数字 面试题 60 : 出现频率最高的 k 个数字 面试题 61 : 和最小的 k 个数对 前言 堆最大的特点是最大值或最小值位于堆的顶部&#xff0c;只需要 O(1) 的时间就可以求出一个数据集合中的最大值或最小值&#xff0c;同时在堆中添加或…

金融保险领域统一消息发送平台

项目介绍&#xff1a;系统为金融保险行业统一推送基础平台&#xff0c;日均推送消息2000万条&#xff0c;支持短信、官微、邮件多种发送渠道。 项目定位&#xff1a;结合公司项目产品化战略&#xff0c;从基础的推送能力出发&#xff0c;形成公司的统一推送基础产品 项目功能…