零基础设计模式——行为型模式 - 模板方法模式

第四部分:行为型模式 - 模板方法模式 (Template Method Pattern)

现在我们来学习模板方法模式。这个模式在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中实现。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

  • 核心思想:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

模板方法模式 (Template Method Pattern)

“定义一个操作中的算法的骨架(骨架步骤的顺序),而将一些步骤(具体实现)延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。” (Define the skeleton of an algorithm in an operation, deferring some steps to subclasses. Template Method lets subclasses redefine certain steps of an algorithm without changing the algorithm’s structure.)

想象一下制作一杯饮料(比如咖啡或茶)的过程:

  1. 把水烧开 (Boil Water) - 这是一个通用步骤。
  2. 冲泡 (Brew) - 咖啡是冲泡咖啡粉,茶是浸泡茶叶。这是可变步骤。
  3. 把饮料倒进杯子 (Pour in Cup) - 这是一个通用步骤。
  4. 加调料 (Add Condiments) - 咖啡可能加糖和牛奶,茶可能加柠檬。这是可变步骤,并且可能是可选的。

模板方法模式就是把这个制作流程(算法骨架)定义在一个抽象类(比如 BeverageMaker)的模板方法(比如 prepareBeverage())中:

prepareBeverage() {boilWater();      // 具体方法,父类实现brew();           // 抽象方法,子类实现pourInCup();      // 具体方法,父类实现if (customerWantsCondiments()) { // 钩子方法,子类可覆盖addCondiments(); // 抽象方法,子类实现}
}
  • boilWater()pourInCup() 是所有饮料制作共通的,可以在父类中直接实现。
  • brew()addCondiments() 是具体饮料不同的,声明为抽象方法,由子类(如 CoffeeMakerTeaMaker)去实现。
  • customerWantsCondiments() 是一个“钩子 (hook)”方法,父类可以提供一个默认实现(比如默认需要调料),子类可以覆盖它来改变算法的某个特定点(比如某个子类饮料默认不需要调料,或者提供更复杂的判断逻辑)。

这样,算法的整体结构(烧水 -> 冲泡 -> 倒杯 -> 可能加调料)被固定下来了,但具体的冲泡内容和调料可以由子类自由定义。

1. 目的 (Intent)

模板方法模式的主要目的:

  1. 定义算法骨架:在一个抽象类中定义一个算法的框架,明确算法的执行步骤和顺序。
  2. 延迟实现可变步骤:将算法中可变的步骤的实现推迟到子类中。
  3. 代码复用:将算法中不变的部分(公共步骤)放在父类中,避免在子类中重复代码。
  4. 控制子类扩展:父类通过模板方法控制算法的整体流程,子类只能在指定的可变点上进行扩展,而不能改变算法的结构。

2. 生活中的例子 (Real-world Analogy)

  • 食谱 (Recipe)

    • 模板方法:一道菜的烹饪流程(准备食材、切菜、炒制、调味、装盘)。
    • 具体步骤:不同的菜肴在“准备食材”、“炒制方法”、“调味料”等方面有不同的实现。
  • 房屋建造流程

    • 模板方法:打地基 -> 建墙体 ->封顶 -> 内部装修 -> 外部装修。
    • 具体步骤:不同的房屋类型(别墅、公寓)在“墙体材料”、“装修风格”等方面有不同的实现。
  • 软件开发生命周期 (SDLC)

    • 模板方法:需求分析 -> 设计 ->编码 -> 测试 -> 部署 -> 维护。
    • 具体步骤:不同的项目或团队可能在“设计方法论”(如敏捷、瀑布)、“测试策略”等方面有不同的具体做法。
  • 简历模板

    • 模板方法:个人信息 -> 教育背景 -> 工作经历 -> 项目经验 -> 技能证书。
    • 具体步骤:每个人填写的具体内容不同。

3. 结构 (Structure)

模板方法模式通常包含以下角色:

  1. AbstractClass (抽象类)

    • 定义一个或多个抽象操作(primitiveOperation),由具体子类实现这些操作。
    • 定义一个模板方法(templateMethod),该方法实现了一个算法的骨架。模板方法会调用抽象操作、具体操作以及钩子操作。
    • 可以包含一些具体操作(concreteOperation),这些操作对所有子类都是相同的。
    • 可以包含钩子操作(hookOperation),通常在抽象类中提供一个默认实现(或者为空实现),子类可以根据需要覆盖它们来影响算法的流程。
  2. ConcreteClass (具体类)

    • 继承自 AbstractClass。
    • 实现父类中定义的抽象操作。
    • 可以覆盖父类中的钩子操作。
      在这里插入图片描述

模板方法中的操作类型

  • 具体操作 (Concrete Operations):在抽象类中实现,子类可以直接使用或继承。
  • 抽象操作 (Abstract Operations / Primitive Operations):在抽象类中声明(通常为抽象方法),必须由子类实现。
  • 钩子操作 (Hook Operations):在抽象类中提供一个默认实现(可能是空实现)。子类可以选择性地覆盖这些方法,以在算法的特定点“挂钩”并改变算法的行为。钩子常用于:
    • 让子类能够对算法的某一步进行可选的扩展。
    • 让子类能够决定算法的某一步是否执行。

模板方法通常被声明为 final (Java) 或在 Go 中通过非导出方法和导出方法组合的方式(父类调用非导出的可被子类“覆盖”的方法)来防止子类改变算法的整体结构。

4. 适用场景 (When to Use)

  • 当你想一次性实现一个算法的不变部分,并将可变的行为留给子类来实现时。
  • 当各个子类中公共的行为应被提取出来并集中到一个公共父类中以避免代码重复时。首先识别现有代码中的不同之处,然后将不同之处分离为新的操作。最后,用一个调用这些新操作的模板方法来替换这些不同的代码。
  • 当需要控制子类的扩展时。模板方法只在特定点调用钩子操作,这就允许在这些点进行扩展,而不是在其他地方。
  • 框架开发:框架通常定义了整体的执行流程(模板方法),而应用程序开发者通过继承框架中的类并实现特定的抽象方法或钩子方法来定制应用行为。

5. 优缺点 (Pros and Cons)

优点:

  1. 代码复用:将公共代码放在抽象父类中,提高了代码复用性。
  2. 封装不变部分,扩展可变部分:算法的骨架(不变部分)在父类中定义和控制,可变部分由子类实现,易于扩展。
  3. 符合开闭原则:对扩展开放(可以通过增加子类来实现新的行为),对修改关闭(不需要修改父类的模板方法)。
  4. 控制反转 (Inversion of Control - IoC):父类调用子类的操作,而不是子类调用父类。这是一种简单的 IoC 形式,有时被称为“好莱坞原则”——“不要给我们打电话,我们会给你打电话 (Don’t call us, we’ll call you)”。

缺点:

  1. 类的数量增加:每个不同的算法变体都需要一个单独的子类,可能会导致类的数量增多。
  2. 继承的限制:模板方法模式基于继承,这带来了一些固有的限制。例如,子类必须继承抽象父类,如果子类已经有了其他父类(在不支持多重继承的语言中),则无法使用此模式。
  3. 算法骨架固定:算法的整体流程由父类固定,如果需要对流程本身进行大的改动,可能会比较困难,可能需要修改父类的模板方法。
  4. 可读性可能降低:如果模板方法中的步骤过多,或者钩子方法的使用比较复杂,可能会使得理解算法的整体流程和子类的具体实现之间的关系变得困难。

6. 实现方式 (Implementations)

让我们以制作不同类型的报告(例如,文本报告和HTML报告)为例。报告生成的流程可能包括:初始化、格式化头部、格式化主体内容、格式化尾部、输出。

抽象类 (ReportGenerator)
// report_generator.go (AbstractClass and its methods)
package templateimport "fmt"// ReportGenerator 抽象类 (通过接口和嵌入结构体模拟)
// Go 中没有直接的抽象类,通常用接口定义行为,用结构体嵌入实现通用部分
// 或者定义一个包含未实现方法的结构体,让具体子类去“填充”这些方法。
// 这里我们采用一种更接近传统模板方法模式的结构:
// 一个包含模板方法的结构体,它调用由嵌入的“子类”接口实现的方法。// ReportSteps 定义了子类需要实现的步骤
type ReportSteps interface {initialize()formatHeader() stringformatBody() stringformatFooter() stringoutputReport(header, body, footer string)hookBeforeBody() // 钩子方法
}// BaseReportGenerator 包含模板方法和通用逻辑
type BaseReportGenerator struct {steps ReportSteps // 指向具体实现这些步骤的对象 (子类)
}// NewBaseReportGenerator 构造函数,需要传入具体的步骤实现者
func NewBaseReportGenerator(steps ReportSteps) *BaseReportGenerator {return &BaseReportGenerator{steps: steps}
}// GenerateReport 模板方法
func (rg *BaseReportGenerator) GenerateReport() {rg.steps.initialize() // 调用子类实现的初始化header := rg.steps.formatHeader() // 调用子类实现的头部格式化rg.steps.hookBeforeBody() // 调用钩子方法body := rg.steps.formatBody() // 调用子类实现的身体格式化footer := rg.steps.formatFooter() // 调用子类实现的尾部格式化rg.steps.outputReport(header, body, footer) // 调用子类实现的输出fmt.Println("Report generation complete.")
}// --- 为了让具体类能调用到BaseReportGenerator的方法,或者让BaseReportGenerator能调用具体类的方法
// Go 的实现方式与 Java/C++ 的继承有区别。一种常见做法是具体类持有 BaseReportGenerator,
// 或者 BaseReportGenerator 持有具体步骤的实现者(如上面的 ReportSteps)。
// 下面的具体类将实现 ReportSteps 接口。
// ReportGenerator.java (AbstractClass)
package com.example.template;public abstract class ReportGenerator {// Template method - final to prevent overriding the algorithm structurepublic final void generateReport() {initialize(); // Common step, or could be abstract/hookString header = formatHeader(); // Step to be implemented by subclasshookBeforeBody(); // Hook methodString body = formatBody(); // Step to be implemented by subclassString footer = formatFooter(); // Step to be implemented by subclassoutputReport(header, body, footer); // Common step, or could be abstract/hookSystem.out.println("Report generation complete.");}// Common methods (can be overridden if not final)protected void initialize() {System.out.println("ReportGenerator: Initializing common report data...");}protected void outputReport(String header, String body, String footer) {System.out.println("ReportGenerator: --- Final Report ---");System.out.println(header);System.out.println(body);System.out.println(footer);System.out.println("ReportGenerator: --- End of Report ---");}// Abstract methods (primitive operations) - to be implemented by subclassesprotected abstract String formatHeader();protected abstract String formatBody();protected abstract String formatFooter();// Hook method - subclass can override, but not mandatoryprotected void hookBeforeBody() {// Default implementation does nothingSystem.out.println("ReportGenerator: (Hook) No specific action before body by default.");}
}
具体类 (TextReportGenerator, HtmlReportGenerator)
// text_report_generator.go (ConcreteClass)
package templateimport "fmt"// TextReportGenerator 具体类
type TextReportGenerator struct {// 可以嵌入 BaseReportGenerator 来继承其方法,但Go的模板方法通常不这么做// 或者让 BaseReportGenerator 持有 TextReportGenerator 的实例 (通过 ReportSteps 接口)data string // 示例数据
}func NewTextReportGenerator(data string) *TextReportGenerator {return &TextReportGenerator{data: data}
}// 实现 ReportSteps 接口
func (tr *TextReportGenerator) initialize() {fmt.Println("TextReport: Initializing text report specific data...")
}func (tr *TextReportGenerator) formatHeader() string {return "=== TEXT REPORT HEADER ==="
}func (tr *TextReportGenerator) formatBody() string {return fmt.Sprintf("Body: %s\n(Rendered as plain text)", tr.data)
}func (tr *TextReportGenerator) formatFooter() string {return "--- TEXT REPORT FOOTER ---"
}func (tr *TextReportGenerator) outputReport(header, body, footer string) {fmt.Println("TextReport: Outputting to console:")fmt.Println(header)fmt.Println(body)fmt.Println(footer)
}func (tr *TextReportGenerator) hookBeforeBody() {fmt.Println("TextReport: (Hook) Adding a small note before text body.")
}// html_report_generator.go (ConcreteClass)
package templateimport "fmt"// HtmlReportGenerator 具体类
type HtmlReportGenerator struct {data string
}func NewHtmlReportGenerator(data string) *HtmlReportGenerator {return &HtmlReportGenerator{data: data}
}func (hr *HtmlReportGenerator) initialize() {fmt.Println("HtmlReport: Initializing HTML report specific data (e.g., CSS links)...")
}func (hr *HtmlReportGenerator) formatHeader() string {return "<html>\n<head><title>HTML Report</title></head>\n<body>\n  <h1>HTML Report Header</h1>"
}func (hr *HtmlReportGenerator) formatBody() string {return fmt.Sprintf("  <p>Body: %s</p>\n  <em>(Rendered as HTML)</em>", hr.data)
}func (hr *HtmlReportGenerator) formatFooter() string {return "  <hr/>\n  <footer>HTML Report Footer</footer>\n</body>\n</html>"
}func (hr *HtmlReportGenerator) outputReport(header, body, footer string) {fmt.Println("HtmlReport: Simulating saving to an HTML file:")fmt.Println(header)fmt.Println(body)fmt.Println(footer)
}func (hr *HtmlReportGenerator) hookBeforeBody() {// HTML报告可能不需要这个钩子,或者有不同的实现fmt.Println("HtmlReport: (Hook) Adding a meta tag or script before HTML body.")
}
// TextReportGenerator.java (ConcreteClass)
package com.example.template;public class TextReportGenerator extends ReportGenerator {private String data;public TextReportGenerator(String data) {this.data = data;}@Overrideprotected void initialize() {super.initialize(); // Call common initialization if neededSystem.out.println("TextReport: Initializing text report specific data...");}@Overrideprotected String formatHeader() {return "=== TEXT REPORT HEADER ===";}@Overrideprotected String formatBody() {return "Body: " + this.data + "\n(Rendered as plain text)";}@Overrideprotected String formatFooter() {return "--- TEXT REPORT FOOTER ---";}@Overrideprotected void outputReport(String header, String body, String footer) {// Override if text report needs a different output mechanismSystem.out.println("TextReport: Outputting to console:");System.out.println(header);System.out.println(body);System.out.println(footer);}@Overrideprotected void hookBeforeBody() {System.out.println("TextReport: (Hook) Adding a small note before text body.");}
}// HtmlReportGenerator.java (ConcreteClass)
package com.example.template;public class HtmlReportGenerator extends ReportGenerator {private String data;public HtmlReportGenerator(String data) {this.data = data;}@Overrideprotected String formatHeader() {return "<html>\n<head><title>HTML Report</title></head>\n<body>\n  <h1>HTML Report Header</h1>";}@Overrideprotected String formatBody() {return "  <p>Body: " + this.data + "</p>\n  <em>(Rendered as HTML)</em>";}@Overrideprotected String formatFooter() {return "  <hr/>\n  <footer>HTML Report Footer</footer>\n</body>\n</html>";}// We can use the default initialize() and outputReport() from ReportGenerator// or override them if specific HTML logic is needed.// Override hook if needed@Overrideprotected void hookBeforeBody() {System.out.println("HtmlReport: (Hook) Adding a meta tag or script before HTML body.");}
}
客户端使用
// main.go (示例用法)
/*
package mainimport ("./template""fmt"
)func main() {fmt.Println("--- Generating Text Report ---")textData := "This is the data for the text report."textReportSteps := template.NewTextReportGenerator(textData)textReportGenerator := template.NewBaseReportGenerator(textReportSteps)textReportGenerator.GenerateReport()fmt.Println("\n--- Generating HTML Report ---")htmlData := "This is the data for the HTML report."htmlReportSteps := template.NewHtmlReportGenerator(htmlData)htmlReportGenerator := template.NewBaseReportGenerator(htmlReportSteps)htmlReportGenerator.GenerateReport()
}
*/
// Main.java (示例用法)
/*
package com.example;import com.example.template.HtmlReportGenerator;
import com.example.template.ReportGenerator;
import com.example.template.TextReportGenerator;public class Main {public static void main(String[] args) {System.out.println("--- Generating Text Report ---");String textData = "This is the data for the text report.";ReportGenerator textReport = new TextReportGenerator(textData);textReport.generateReport();System.out.println("\n--- Generating HTML Report ---");String htmlData = "This is the data for the HTML report.";ReportGenerator htmlReport = new HtmlReportGenerator(htmlData);htmlReport.generateReport();}
}
*/

7. 总结

模板方法模式是一种基于继承的代码复用技术。它允许我们定义一个算法的骨架,并将算法中某些步骤的实现延迟到子类。这使得子类可以在不改变算法整体结构的前提下,重新定义算法的特定步骤。该模式在框架设计中非常常见,因为它提供了一种标准化的方式来构建可扩展的组件。通过合理使用抽象方法和钩子方法,可以在固定算法流程和提供灵活性之间找到一个很好的平衡点。

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

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

相关文章

android通过adb push apk放置目录/sdcard/Download/下无法安装

本文通过对源码进行追踪,并且调试各种方式,得出android通过adb push apk放置目录/sdcard/Download/下无法安装的原因,并从两个修改点触发,提出如何能修复此问题的建议。 1. 现象 把apk通过adb push的方式放在/sdcard/Download文件夹下, (1)直接打开File(DocumentUI)…

Spring Boot整合PF4J:构建动态插拔的组件化架构

前言 在当今快速迭代的软件开发领域,业务需求的频繁变更对系统架构的灵活性和可扩展性提出了极高要求。传统的单体应用架构在面对功能的不断新增和修改时,往往会陷入代码臃肿、维护困难、扩展性差的困境。组件化开发,为解决这些问题提供了新的思路,通过实现组件的动态插拔…

剃须效率低?电铸多孔刀网设计如何提升毛发捕捉率

剃须效率低下常源于刀网对毛发的捕捉能力不足——传统冲压刀网因孔型单一、边缘毛刺等问题&#xff0c;导致胡须滑脱或拉扯。而电铸多孔刀网通过精密工艺革新&#xff0c;将毛发捕捉率提升40%以上。其核心优势在于三维立体孔型设计与微米级精度控制&#xff0c;以下是技术解析&…

进一步了解git

1、什么是集中式&#xff1f;什么是分布式&#xff1f; SVN&#xff08;集中式&#xff09; 单一中央仓库&#xff1a;所有代码和历史版本集中存储在中央服务器&#xff0c;用户本地仅保存当前工作副本。 强依赖网络&#xff1a;提交、查看历史等操作需实时连接服务器&#xf…

一、react18+项目初始化

npx create-rect-app 项目名称配置antd design mobile // 安装 npm install --save antd-mobile // 在文件中直接引入使用 import { Button } from antd-mobile <Button></Button>更改webpack配置 // 1.安装必要的包 npm install craco --save-dev // 2.修改pack…

Azure 资源清单

Azure 资源清单 作用前置条件安装PowerShell 7.0验证 Azure资源清单安装配置如果有旧版本&#xff0c;导致新模块安装不上&#xff0c;进行强制安装 PowerShell 登录到 Azure基本命令输出详细信息效果图展示 作用 官方文档&#xff1a;https://github.com/microsoft/ARI?tabr…

S11的含义-信号完整性分析

S11的含义: PCB上的互连结构是线性无源的&#xff0c;在传输信号时激励源只有一个&#xff0c;即驱动器发出的信号。如果正弦信号从端口1进入&#xff0c;根据S11定义&#xff0c;S11表示端口1出来的正弦信号和端口1进入的正弦信号的比值。工程上通常把S11称为回波损耗(Return …

基于OpenCv(开源计算机视觉库)的图像旋转匹配

OpenCV&#xff08;Open Source Computer Vision Library&#xff09;是一个开源的计算机视觉和机器学习软件库&#xff0c;具有跨平台特性&#xff0c;广泛应用于工业检测、医疗影像分析、自动驾驶、无人机、机器人视觉等多个领域。 本项目解决了图像模板匹配时的旋转问题。传…

Elasticsearch Open Inference API 新增对 Cohere 的 Rerank 3 模型支持

作者&#xff1a;来自 Elastic Serena Chou 及 Max Hniebergall 了解 Cohere reranking&#xff0c;如何将 Cohere 的 Rerank 3 模型与 Elasticsearch 的 open inference API 一起使用&#xff0c;以及 Elastic 在语义 reranking 方面的路线图。 注&#xff1a;原文在 2024 年 …

九日集训第六天

目录 两个数对之间最大的乘积差 三角形的最大周长 数组拆分 救生艇 摆动排序|| 分发饼干 最少操作使数组递增 使数组唯一的最小增量 有效三角形的个数 两个数对之间最大的乘积差 class Solution { public:int maxProductDifference(vector<int>& nums) {so…

【软件工程】Waitress + Nginx 部署 Python Web 服务

下面是完整的 Windows 系统部署方案,使用 Waitress 作为 WSGI 服务器运行 Python 后端,Nginx 作为反向代理同时提供前端服务: 项目结构 text 复制 下载 myapp/ ├── backend/ # Python后端 │ ├── app.py # Flask应用入口 │ ├──…

JS数据类型检测方法总结

在 JavaScript 中&#xff0c;数据类型检测是开发中的常见需求。以下是主要检测方法及其优缺点&#xff1a; 1. typeof 操作符 最基础的检测方式&#xff0c;返回类型字符串&#xff1a; typeof 42; // "number" typeof "hello"; // &qu…

AEO:从搜索引擎到答案引擎,AI时代搜索优化的新战场

在 ChatGPT、DeepSeek、Google SGE 等生成式AI崛起的时代&#xff0c;搜索正在经历一场根本性变革&#xff1a; 过去&#xff1a;搜索引擎优化&#xff08;SEO&#xff09; 现在&#xff1a;答案引擎优化&#xff08;AEO&#xff09; 当搜索结果开始由AI直接生成“答案”而非…

搭建Node.js服务器

1.基础HTTP服务器: 添加了路由处理添加了404错误处理添加了服务器错误监听 2.静态资源服务器: 使用异步文件操作支持目录自动索引(默认加载 index.html)自动检测文件类型并设置正确Content-Type更完善的错误处理 3.处理GET请求参数 提供了一个HTML表单用于测试使用url模块…

Linux grep 命令

grep 是 Linux/Unix 系统中用于文本搜索的强大工具&#xff0c;支持基于正则表达式的模式匹配。以下是其详细用法及实际应用示例&#xff1a; 基本语法 grep [选项] 模式 [文件...]模式&#xff1a;要搜索的字符串或正则表达式。文件&#xff1a;可以是单个文件或多个文件&…

oracle 11g通过rman做备份和还原

ORACLE RMAN增量备份完整恢复测试 1.创建测试环境: 1.1.创建测试表空间 SQL> create tablespace tablespace1 datafile ‘/data/u01/app/oracle/oradata/orcl/tablespace1.dbf’ size 10m; SQL> 1.2.创建测试用户并指定为默认表空间: SQL> create user user1 iden…

为什么TCP有粘包问题,而UDP没有

TCP粘包问题源于其面向字节流的设计&#xff0c;而UDP无此问题因其基于数据报的传输机制。 &#x1f50d; 一、TCP粘包问题的原因 字节流传输特性 TCP将数据视为连续的字节流&#xff0c;而非独立的消息包。发送端多次写入的小数据可能被合并为一个TCP段发送&#xff1b;接收端…

ELM:Embodied Understanding of Driving Scenarios

1. ELM 的创新点与核心思路 ELM 的核心在于 “具身理解”(Embodied Understanding),即通过常识与环境交互并进行推理,这一理念适用于自动驾驶车辆、机器人和无人机等多种应用场景。具身智能体(Embodied Agent)需具备四大核心能力:首先,它能够描述周围环境,对交通物体的…

实景VR知识科普

实景VR的定义与技术特点 实景VR&#xff0c;即基于真实场景的虚拟现实技术&#xff0c;是通过计算机生成的三维环境&#xff0c;旨在模拟并再现真实世界场景。用户佩戴VR设备&#xff08;如VR头盔、手柄等&#xff09;后&#xff0c;能够沉浸在一个高度仿真的虚拟环境中&#…

CppCon 2016 学习:ITERATOR HAIKU

这组幻灯片讲解了 C 中**范围&#xff08;Ranges&#xff09;和迭代器&#xff08;Iterators&#xff09;**的核心概念&#xff0c;特别是 C14 标准定义的五种迭代器类别&#xff0c;以及范围的基本使用方式。我帮你理个思路&#xff1a; 1. RANGE-SEQUENCE: 元素范围&#xf…