• 文件浏览器
  • 000 《Folly 库知识框架》 001 《folly::Utility 权威指南》 002 《folly::Preprocessor 权威指南》 003 《Folly::Traits 权威指南:C++ 元编程的基石》 004 《Folly::ScopeGuard 权威指南:C++ 作用域资源管理利器》 005 《Folly Singleton 权威指南:从入门到精通》 007 《Folly Dynamic.h 权威指南:C++ 动态类型实战》 008 《Folly Optional.h 权威指南:从入门到精通》 009 《Folly Expected.h 权威指南》 010 《Folly Try.h 权威指南:C++ 异常处理的现代实践》 011 《Folly Variant.h 权威指南》 012 《folly::Vector 权威指南: 深度探索与实践》 013 《Folly Map 权威指南:从入门到精通》 014 《Folly Set 权威指南》 015 《Folly SmallVector 权威指南》 016 《Folly Allocator.h 权威指南:C++ 高性能内存管理深度解析》 017 《Folly Foreach.h 权威指南:从入门到精通》 018 《folly/futures 权威指南:Future 和 Promise 深度解析与实战》 019 《Folly Executor 权威指南:从入门到精通 (Folly Executor: The Definitive Guide from Beginner to Expert)》 020 《深入浅出 Folly Fibers:FiberManager 和 Fiber 权威指南》 021 《folly EventBase.h 编程权威指南》 022 《Folly Baton.h 权威指南:C++ 高效线程同步实战》 023 《深入探索 folly/Synchronized.h:并发编程的基石 (In-depth Exploration of folly/Synchronized.h: The Cornerstone of Concurrent Programming)》 024 《folly/SpinLock.h 权威指南:原理、应用与最佳实践》 025 《Folly SharedMutex.h 权威指南:原理、应用与实战》 026 《Folly AtomicHashMap.h 权威指南:从入门到精通》 027 《Folly/IO 权威指南:高效网络编程实战》 028 《folly/Uri.h 权威指南 (Folly/Uri.h: The Definitive Guide)》 029 《Folly String.h 权威指南:深度解析、实战应用与高级技巧》 030 《folly/Format.h 权威指南 (The Definitive Guide to folly/Format.h)》 031 《Folly Conv.h 权威指南:C++ 高效类型转换详解》 032 《folly/Unicode.h 权威指南:深入探索与实战应用》 033 《folly/json.h 权威指南》 034 《Folly Regex.h 权威指南:从入门到精通 (Folly Regex.h: The Definitive Guide from Beginner to Expert)》 035 《Folly Clock.h 权威指南:系统、实战与深度解析》 036 《folly/Time.h 权威指南:C++ 时间编程实战》 037 《Folly Chrono.h 权威指南》 038 《Folly ThreadName.h 权威指南:系统线程命名深度解析与实战》 039 《Folly OptionParser.h 权威指南》 040 《C++ Range.h 实战指南:从入门到专家》 041 《Folly File.h 权威指南:从入门到精通》 042 《Folly/xlog.h 权威指南:从入门到精通》 043 《Folly Trace.h 权威指南:从入门到精通 (Folly Trace.h: The Definitive Guide from Beginner to Expert)》 044 《Folly Demangle.h 权威指南:C++ 符号反解的艺术与实践 (Folly Demangle.h: The Definitive Guide to C++ Symbol Demangling)》 045 《folly/StackTrace.h 权威指南:原理、应用与最佳实践 (folly/StackTrace.h Definitive Guide: Principles, Applications, and Best Practices)》 046 《Folly Test.h 权威指南:C++ 单元测试实战 (Folly Test.h: The Definitive Guide to C++ Unit Testing in Practice)》 047 《《Folly Benchmark.h 权威指南 (Folly Benchmark.h: The Definitive Guide)》》 048 《Folly Random.h 权威指南:C++随机数生成深度解析》 049 《Folly Numeric.h 权威指南》 050 《Folly Math.h 权威指南:从入门到精通 (Folly Math.h: The Definitive Guide from Beginner to Expert)》 051 《Folly FBMath.h 权威指南:从入门到精通 (Folly FBMath.h: The Definitive Guide - From Beginner to Expert)》 052 《Folly Cursor.h 权威指南:高效数据读取与解析 (Folly Cursor.h Authoritative Guide: Efficient Data Reading and Parsing)》 053 《Folly与Facebook Thrift权威指南:从入门到精通 (Folly and Facebook Thrift: The Definitive Guide from Beginner to Expert)》 054 《Folly CPUThreadPoolExecutor.h 权威指南:原理、实践与高级应用》 055 《Folly HardwareConcurrency.h 权威指南:系统级并发编程基石》

    015 《Folly SmallVector 权威指南》


    作者Lou Xiao, gemini创建时间2025-04-17 01:21:41更新时间2025-04-17 01:21:41

    🌟🌟🌟本文案由Gemini 2.0 Flash Thinking Experimental 01-21创作,用来辅助学习知识。🌟🌟🌟

    书籍大纲

    ▮▮▮▮ 1. chapter 1: 走进 Folly SmallVector(走进 Folly SmallVector)
    ▮▮▮▮▮▮▮ 1.1 初识 Folly 与 SmallVector(初识 Folly 与 SmallVector):Folly 库概览及其在现代 C++ 开发中的作用
    ▮▮▮▮▮▮▮ 1.2 SmallVector 的设计哲学与优势(SmallVector 的设计哲学与优势):对比 std::vector,解析 SmallVector 在特定场景下的性能优势和适用性
    ▮▮▮▮▮▮▮ 1.3 SmallVector 的应用场景分析(SmallVector 的应用场景分析):从嵌入式系统到高性能服务器,SmallVector 的用武之地
    ▮▮▮▮▮▮▮ 1.4 环境搭建与快速上手(环境搭建与快速上手):如何引入 Folly 库,编写你的第一个 SmallVector 程序
    ▮▮▮▮ 2. chapter 2: SmallVector 基础:核心概念与用法(SmallVector 基础:核心概念与用法)
    ▮▮▮▮▮▮▮ 2.1 模板参数详解:Capacity 与 Size(模板参数详解:Capacity 与 Size):深入理解 SmallVector 的内存布局,静态容量与动态增长
    ▮▮▮▮▮▮▮ 2.2 构造、析构与赋值:对象的生命周期管理(构造、析构与赋值:对象的生命周期管理):各种构造函数、拷贝与移动语义、析构行为详解
    ▮▮▮▮▮▮▮ 2.3 元素访问与迭代器(元素访问与迭代器):operator[]、at()、front()、back(),begin()、end()、rbegin()、rend() 的使用
    ▮▮▮▮▮▮▮ 2.4 容量管理与元素操作(容量管理与元素操作):size()、capacity()、empty()、reserve()、shrink_to_fit(),push_back()、pop_back()、insert()、erase()、clear() 等常用 API
    ▮▮▮▮▮▮▮▮▮▮▮ 2.4.1 实战代码:动态数组的实现(实战代码:动态数组的实现):使用 SmallVector 实现一个可动态增长的数组类
    ▮▮▮▮▮▮▮▮▮▮▮ 2.4.2 最佳实践:选择合适的初始容量(最佳实践:选择合适的初始容量):避免不必要的内存分配和拷贝
    ▮▮▮▮▮▮▮ 2.5 类型安全与异常处理(类型安全与异常处理):SmallVector 的类型安全性,异常安全编程实践
    ▮▮▮▮ 3. chapter 3: SmallVector 进阶:深入原理与高级特性(SmallVector 进阶:深入原理与高级特性)
    ▮▮▮▮▮▮▮ 3.1 内存分配策略:栈上空间与堆上空间(内存分配策略:栈上空间与堆上空间):SmallVector 的内存管理机制深度剖析
    ▮▮▮▮▮▮▮ 3.2 移动语义与性能优化(移动语义与性能优化):利用移动语义减少拷贝开销,提升性能
    ▮▮▮▮▮▮▮ 3.3 emplace_back 与 in-place 构造(emplace_back 与 in-place 构造):高效元素插入方法,避免临时对象产生
    ▮▮▮▮▮▮▮ 3.4 自定义分配器(Custom Allocator)的应用(自定义分配器(Custom Allocator)的应用):高级内存管理技巧,定制化内存分配策略
    ▮▮▮▮▮▮▮ 3.5 与 std::vector 的性能对比评测(与 std::vector 的性能对比评测):基准测试与性能分析,量化 SmallVector 的优势
    ▮▮▮▮▮▮▮▮▮▮▮ 3.5.1 benchmark 代码示例(benchmark 代码示例):使用 Google Benchmark 或其他工具进行性能测试
    ▮▮▮▮ 4. chapter 4: SmallVector 高级应用与实战案例(SmallVector 高级应用与实战案例)
    ▮▮▮▮▮▮▮ 4.1 在高性能计算中的应用(在高性能计算中的应用):利用 SmallVector 优化计算密集型应用
    ▮▮▮▮▮▮▮ 4.2 在网络编程中的应用(在网络编程中的应用):高效处理网络数据包,提升网络服务性能
    ▮▮▮▮▮▮▮ 4.3 在游戏开发中的应用(在游戏开发中的应用):优化游戏引擎的数据结构,提升帧率
    ▮▮▮▮▮▮▮ 4.4 与其他 Folly 组件的协同工作(与其他 Folly 组件的协同工作):例如 FBString, FBVector 等
    ▮▮▮▮▮▮▮ 4.5 案例分析:开源项目中的 SmallVector 应用(案例分析:开源项目中的 SmallVector 应用):学习知名开源项目如何使用 SmallVector
    ▮▮▮▮ 5. chapter 5: SmallVector API 全面解析(SmallVector API 全面解析)
    ▮▮▮▮▮▮▮ 5.1 构造函数与赋值运算符详解(构造函数与赋值运算符详解):深入剖析各种构造函数和赋值运算符的参数、返回值和异常
    ▮▮▮▮▮▮▮ 5.2 容量函数详解(容量函数详解):size()、capacity()、max_size()、empty()、reserve()、shrink_to_fit()
    ▮▮▮▮▮▮▮ 5.3 元素访问函数详解(元素访问函数详解):operator[]、at()、front()、back()、data()
    ▮▮▮▮▮▮▮ 5.4 修改器函数详解(修改器函数详解):push_back()、emplace_back()、pop_back()、insert()、emplace()、erase()、clear()、assign()、resize()、swap()
    ▮▮▮▮▮▮▮ 5.5 迭代器函数详解(迭代器函数详解):begin()、end()、cbegin()、cend()、rbegin()、rend()、crbegin()、crend()
    ▮▮▮▮ 6. chapter 6: SmallVector 常见问题与最佳实践(SmallVector 常见问题与最佳实践)
    ▮▮▮▮▮▮▮ 6.1 常见错误用法与陷阱(常见错误用法与陷阱):避免 SmallVector 使用中的常见错误
    ▮▮▮▮▮▮▮ 6.2 性能调优技巧(性能调优技巧):提升 SmallVector 应用性能的实用技巧
    ▮▮▮▮▮▮▮ 6.3 与其他容器的选择:std::vector, std::array, std::deque 等(与其他容器的选择:std::vector, std::array, std::deque 等):根据不同场景选择最合适的容器
    ▮▮▮▮▮▮▮ 6.4 SmallVector 的未来发展趋势(SmallVector 的未来发展趋势):展望 SmallVector 的发展方向和潜在改进
    ▮▮▮▮ 7. chapter 7: 总结与展望(总结与展望)
    ▮▮▮▮▮▮▮ 7.1 SmallVector 的价值与意义回顾(SmallVector 的价值与意义回顾)
    ▮▮▮▮▮▮▮ 7.2 持续学习与深入探索 Folly 库(持续学习与深入探索 Folly 库)


    1. chapter 1: 走进 Folly SmallVector(走进 Folly SmallVector)

    1.1 初识 Folly 与 SmallVector(初识 Folly 与 SmallVector):Folly 库概览及其在现代 C++ 开发中的作用

    在现代 C++ 开发的浩瀚星空中,Folly 库犹如一颗璀璨的明星,以其卓越的性能和丰富的功能,照亮了无数工程师的编程之路。Folly,全称 "Facebook Open-source Library",是由 Facebook 开源的一套高度模块化的 C++ 库。它并非一个大而全的框架,而是一系列专注于解决实际工程问题的工具和组件的集合。Folly 库的设计哲学是拥抱现代 C++ 标准,追求极致的性能和效率,并提供在标准库之外的实用工具,以应对高性能、高并发场景下的挑战。

    Folly 库涵盖了广泛的领域,从基础的数据结构与算法,到并发编程、网络编程、序列化与反序列化,再到时间处理、字符串操作等等,几乎现代 C++ 开发的方方面面都能在 Folly 中找到相应的解决方案。它不仅仅是对标准库的简单扩展,更是在许多方面进行了创新和优化,例如:

    高性能数据结构Folly 提供了许多高性能的数据结构,例如 FBVectorFBStringF14ValueMapF14FastMap 等,它们在特定场景下比标准库容器拥有更出色的性能。
    强大的并发工具Folly 提供了丰富的并发编程工具,如 FuturesPromisesExecutors 等,使得异步编程和并发控制更加简洁高效。
    高效的网络编程库Folly 包含 Asio 的扩展和封装,以及 Proxygen HTTP 框架,为构建高性能网络应用提供了坚实的基础。
    实用的工具函数Folly 提供了大量的实用工具函数,涵盖字符串处理、时间操作、类型转换、位操作等,极大地提高了开发效率。

    Folly 众多组件中,SmallVector 凭借其独特的内存管理策略和卓越的性能,在各种应用场景中脱颖而出,成为备受关注的焦点。SmallVector 是一种混合型容器(Hybrid Container),它巧妙地结合了静态数组(Static Array)动态数组(Dynamic Array)的优点。与 std::vector 总是将元素存储在堆(Heap)上不同,SmallVector 尝试将元素存储在栈(Stack)上的一小块预留空间中,只有当元素数量超过预留空间时,才会退化为在堆上分配内存的动态数组。这种设计使得 SmallVector 在元素数量较小时,能够避免堆内存分配和释放的开销,从而获得更高的性能。

    SmallVector 的出现并非要完全替代 std::vector,而是在特定场景下提供一种更优的选择。它尤其适用于以下情况:

    元素数量通常较小:当你知道容器中存储的元素数量通常在一个较小的范围内时,SmallVector 可以充分发挥其栈上存储的优势,避免堆内存操作的开销。
    对性能有较高要求:在性能敏感的应用中,例如游戏开发、实时系统、高性能计算等,SmallVector 可以通过减少内存分配和拷贝,显著提升程序性能。
    内存资源受限:在嵌入式系统或资源受限的环境中,SmallVector 的栈上存储特性可以减少堆内存的使用,降低内存碎片产生的风险。

    总而言之,Folly 库是现代 C++ 开发中不可或缺的利器,而 SmallVector 则是 Folly 库中一颗耀眼的明珠。掌握 SmallVector 的原理和用法,能够帮助我们编写出更加高效、可靠的 C++ 程序,并在各种应用场景中游刃有余。在接下来的章节中,我们将深入探索 SmallVector 的奥秘,从基础概念到高级应用,带你领略 SmallVector 的魅力。

    1.2 SmallVector 的设计哲学与优势(SmallVector 的设计哲学与优势):对比 std::vector,解析 SmallVector 在特定场景下的性能优势和适用性

    为了更好地理解 SmallVector 的设计哲学和优势,我们首先需要将其与 C++ 标准库中最为常用的动态数组容器 std::vector 进行对比。std::vector 以其灵活性和易用性,成为了 C++ 开发中最受欢迎的容器之一。然而,std::vector 的实现方式在某些场景下也存在一定的局限性,而 SmallVector 正是为了弥补这些局限性而诞生的。

    std::vector 的核心设计思想是将元素存储在堆(Heap)上动态分配的内存中。无论 std::vector 存储多少个元素,甚至即使是空的,它都会在堆上分配一块内存来管理这些元素。这种设计保证了 std::vector 可以动态地增长和收缩,适应各种大小的数据集。但是,堆内存的分配和释放相对于栈(Stack)内存操作来说,开销更大。每次在堆上分配内存,都需要操作系统进行内存管理,这涉及到查找空闲内存块、更新内存分配表等操作,而栈内存的分配和释放仅仅是移动栈顶指针,效率非常高。

    SmallVector 的设计哲学正是为了利用栈内存的快速分配和释放特性,来优化小规模数据集的处理性能。SmallVector 的核心思想是:在栈上预留一小块固定大小的内存空间,用于存储容器的前几个元素。只有当元素数量超过栈上空间时,才会在堆上动态分配内存,并将栈上的元素拷贝到堆上。 这种混合内存管理策略使得 SmallVector 在处理小规模数据集时,可以完全避免堆内存分配和释放的开销,从而获得接近于静态数组的性能,同时又保留了动态数组的灵活性。

    为了更清晰地对比 std::vectorSmallVector 的差异,我们可以从以下几个方面进行分析:

    内存分配位置
    ▮▮▮▮⚝ std::vector:始终在堆上分配内存。
    ▮▮▮▮⚝ SmallVector:优先在栈上分配内存,超出容量时退化到堆上。

    内存分配开销
    ▮▮▮▮⚝ std::vector:堆内存分配和释放开销相对较大。
    ▮▮▮▮⚝ SmallVector:小规模数据时,栈内存分配和释放开销极小;大规模数据时,堆内存开销与 std::vector 相当。

    性能特点
    ▮▮▮▮⚝ std::vector:通用性强,适用于各种规模的数据集,但小规模数据性能并非最优。
    ▮▮▮▮⚝ SmallVector:小规模数据性能极佳,接近静态数组;大规模数据性能与 std::vector 接近。

    适用场景
    ▮▮▮▮⚝ std::vector:通用动态数组,适用于大多数场景。
    ▮▮▮▮⚝ SmallVector:元素数量通常较小,对性能有较高要求的场景,例如:
    ▮▮▮▮▮▮▮▮⚝ 存储函数调用的小型参数列表。
    ▮▮▮▮▮▮▮▮⚝ 编译器中的小型符号表。
    ▮▮▮▮▮▮▮▮⚝ 图形渲染中的小型顶点列表。
    ▮▮▮▮▮▮▮▮⚝ 网络编程中的小型数据包头部。

    空间占用
    ▮▮▮▮⚝ std::vector:始终需要在堆上分配内存,即使容器为空,也可能占用一定的堆内存。
    ▮▮▮▮⚝ SmallVector:栈上空间是预先分配的,无论是否使用都会占用栈空间;堆上空间只有在需要时才会分配。

    总结来说,SmallVector 的优势在于:

    ▮▮▮▮ⓐ 性能优势:对于小规模数据集,SmallVector 可以显著提升性能,因为它避免了堆内存分配和释放的开销。栈内存操作的速度远快于堆内存操作,尤其是在频繁创建和销毁容器的场景下,性能提升更为明显。
    ▮▮▮▮ⓑ 内存局部性:栈内存通常在 CPU 缓存中命中率更高,因为栈内存的访问模式往往是连续的,而堆内存的访问模式则可能比较分散。更高的缓存命中率意味着更快的内存访问速度,从而提升程序整体性能。
    ▮▮▮▮ⓒ 避免堆碎片:频繁的堆内存分配和释放容易导致堆碎片,降低内存利用率,甚至影响程序性能。SmallVector 在小规模数据场景下,可以减少堆内存的使用,从而降低堆碎片产生的风险。

    当然,SmallVector 也并非完美无缺。它的栈上空间是固定的,如果预留空间过小,则容易频繁退化到堆上,反而可能降低性能;如果预留空间过大,则会浪费栈内存。因此,选择合适的栈上空间大小是使用 SmallVector 的关键。此外,SmallVector 的适用场景相对有限,主要集中在小规模数据集的处理。对于大规模数据集,std::vector 仍然是更合适的选择。

    在实际应用中,我们需要根据具体的场景和需求,权衡 std::vectorSmallVector 的优缺点,选择最合适的容器。如果你的应用场景符合 SmallVector 的适用条件,那么它将为你带来显著的性能提升。

    1.3 SmallVector 的应用场景分析(SmallVector 的应用场景分析):从嵌入式系统到高性能服务器,SmallVector 的用武之地

    SmallVector 以其独特的性能优势,在各种不同的应用领域都找到了用武之地。从资源受限的嵌入式系统,到追求极致性能的高性能服务器,SmallVector 都能发挥其独特的价值。下面我们将深入分析 SmallVector 在不同场景下的应用,帮助你更好地理解它的适用性和优势。

    ① 嵌入式系统(Embedded Systems)

    嵌入式系统通常具有资源受限的特点,例如内存容量有限、CPU 性能较低等。在这些环境下,内存管理的效率至关重要。std::vector 的堆内存分配方式可能会带来额外的开销,并增加内存碎片产生的风险。而 SmallVector 的栈上存储特性,可以有效地减少堆内存的使用,降低内存分配和释放的开销,并减少内存碎片。

    在嵌入式系统中,SmallVector 可以应用于以下场景:

    小型数据缓冲区:例如,在传感器数据采集、通信协议处理等场景中,经常需要使用小型的数据缓冲区来暂存数据。SmallVector 可以作为高效的数据缓冲区容器,避免频繁的堆内存操作。
    配置参数存储:嵌入式设备的配置参数通常数量较少,且在程序运行期间很少变化。使用 SmallVector 存储配置参数,可以减少堆内存的使用,提高配置参数的访问速度。
    临时数据结构:在某些算法或任务处理过程中,可能需要创建一些临时的、小规模的数据结构。SmallVector 可以作为这些临时数据结构的容器,快速创建和销毁,而无需担心堆内存开销。

    ② 高性能计算(High-Performance Computing, HPC)

    高性能计算领域对程序性能有着极致的追求。在计算密集型应用中,任何细微的性能提升都可能带来巨大的效益。SmallVector 通过减少内存分配和拷贝,可以显著提升程序性能,尤其是在处理小规模数据集时。

    在高性能计算中,SmallVector 可以应用于以下场景:

    小型向量和矩阵:在数值计算、线性代数等领域,经常需要处理小型向量和矩阵。SmallVector 可以作为这些小型向量和矩阵的底层存储容器,提高计算效率。
    临时计算结果存储:在复杂的计算过程中,可能会产生大量的临时计算结果。使用 SmallVector 存储这些临时结果,可以减少内存分配和释放的开销,加速计算过程。
    算法优化:在某些算法中,例如排序、搜索等,可以使用 SmallVector 来优化数据结构,提高算法执行效率。

    ③ 网络编程(Network Programming)

    网络编程对数据处理速度和效率有着很高的要求。在网络服务器中,需要快速处理大量的网络数据包。SmallVector 可以高效地处理网络数据包,提升网络服务性能。

    在网络编程中,SmallVector 可以应用于以下场景:

    网络数据包头部存储:网络数据包的头部通常较小,且结构固定。使用 SmallVector 存储数据包头部,可以快速访问和处理头部信息,提高网络数据包的处理速度。
    小型消息队列:在某些轻量级的网络服务中,可以使用小型消息队列来缓冲网络数据。SmallVector 可以作为高效的消息队列容器,提高消息的入队和出队速度。
    临时数据缓存:在网络数据处理过程中,可能需要临时缓存一些数据。SmallVector 可以作为临时数据缓存容器,快速存储和访问数据。

    ④ 游戏开发(Game Development)

    游戏开发对性能和帧率有着极高的要求。游戏引擎需要处理大量的游戏对象、场景数据、用户输入等。SmallVector 可以优化游戏引擎的数据结构,提升游戏帧率,改善游戏体验。

    在游戏开发中,SmallVector 可以应用于以下场景:

    游戏对象组件存储:游戏对象通常由多个组件构成,例如位置组件、渲染组件、物理组件等。使用 SmallVector 存储游戏对象的组件列表,可以快速访问和管理组件。
    场景图节点存储:游戏场景通常使用场景图来组织和管理游戏对象。SmallVector 可以作为场景图节点的子节点容器,提高场景图的遍历和渲染效率。
    用户输入事件队列:游戏需要快速响应用户的输入事件。使用 SmallVector 作为用户输入事件队列,可以高效地处理用户输入,保证游戏的流畅性。

    ⑤ 其他应用场景

    除了以上列举的场景,SmallVector 还可以应用于其他许多领域,例如:

    编译器开发:编译器可以使用 SmallVector 存储小型符号表、临时数据结构等,提高编译速度。
    数据库系统:数据库系统可以使用 SmallVector 存储小型查询结果集、临时数据缓存等,提高查询效率。
    音视频处理:音视频处理软件可以使用 SmallVector 存储小型音频帧、视频帧等,提高处理速度。

    总而言之,SmallVector 的应用场景非常广泛,只要涉及到小规模数据集的处理,并且对性能有一定要求,SmallVector 都有可能成为一个有力的工具。在实际应用中,我们需要根据具体的场景和需求,评估 SmallVector 的适用性,并进行性能测试,以确定是否能够带来预期的性能提升。

    1.4 环境搭建与快速上手(环境搭建与快速上手):如何引入 Folly 库,编写你的第一个 SmallVector 程序

    要开始使用 Folly 库中的 SmallVector,首先需要搭建开发环境并引入 Folly 库。由于 Folly 库依赖于一些第三方库和工具,环境搭建过程可能相对复杂。但是,一旦环境搭建完成,就可以轻松地使用 Folly 提供的各种强大功能。

    ① 环境准备

    在开始之前,你需要确保你的系统满足以下基本条件:

    操作系统Folly 库主要在 Linux 和 macOS 系统上进行开发和测试,Windows 系统也部分支持,但可能需要额外的配置。
    C++ 编译器Folly 库需要支持 C++14 标准的编译器,推荐使用 GCC 5.0 或更高版本,或者 Clang 3.4 或更高版本。
    CMakeFolly 库使用 CMake 进行构建管理,你需要安装 CMake 3.0 或更高版本。
    PythonFolly 库的构建脚本使用 Python 2.7 或更高版本。
    其他依赖库Folly 库依赖于一些第三方库,例如 Boost、glog、gflags、Double-conversion、Libevent、OpenSSL、zlib、LZ4、Snappy 等。具体的依赖库版本和安装方式,可以参考 Folly 官方文档。

    ② 获取 Folly 库

    你可以从 GitHub 上克隆 Folly 库的源代码:

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 git clone https://github.com/facebook/folly.git
    2 cd folly

    ③ 构建 Folly 库

    folly 目录下,创建一个 build 目录,并使用 CMake 进行配置和构建:

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 mkdir build
    2 cd build
    3 cmake ..
    4 make -j$(nproc) # 使用多核编译加速
    5 sudo make install # 可选,将 folly 安装到系统目录

    在构建过程中,CMake 会自动检测你的系统环境,并下载和安装 Folly 库的依赖项。如果你的系统已经安装了某些依赖库,CMake 可能会直接使用系统已安装的版本。

    ④ 编写你的第一个 SmallVector 程序

    环境搭建完成后,就可以开始编写你的第一个 SmallVector 程序了。创建一个名为 small_vector_example.cpp 的文件,并输入以下代码:

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 #include <folly/container/SmallVector.h>
    2 #include <iostream>
    3
    4 int main() {
    5 // 创建一个 SmallVector,栈上容量为 4,元素类型为 int
    6 folly::SmallVector<int, 4> sv;
    7
    8 // 向 SmallVector 中添加元素
    9 sv.push_back(10);
    10 sv.push_back(20);
    11 sv.push_back(30);
    12 sv.push_back(40);
    13 sv.push_back(50); // 超过栈上容量,将分配堆内存
    14
    15 // 遍历 SmallVector 并打印元素
    16 std::cout << "SmallVector elements: ";
    17 for (int i = 0; i < sv.size(); ++i) {
    18 std::cout << sv[i] << " ";
    19 }
    20 std::cout << std::endl;
    21
    22 // 打印 SmallVector 的容量和大小
    23 std::cout << "Capacity: " << sv.capacity() << std::endl;
    24 std::cout << "Size: " << sv.size() << std::endl;
    25
    26 return 0;
    27 }

    ⑤ 编译和运行程序

    使用 g++ 编译器编译 small_vector_example.cpp 文件,并链接 Folly 库:

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 g++ small_vector_example.cpp -o small_vector_example -lfolly
    2 ./small_vector_example

    如果一切顺利,你将看到以下输出:

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 SmallVector elements: 10 20 30 40 50
    2 Capacity: 6
    3 Size: 5

    这个简单的例子演示了如何创建一个 SmallVector 对象,向其中添加元素,遍历元素,并获取容器的容量和大小。

    ⑥ 代码解析

    #include <folly/container/SmallVector.h>: 引入 SmallVector 的头文件。
    folly::SmallVector<int, 4> sv;: 创建一个 SmallVector 对象 sv。模板参数 int 指定元素类型为 int,模板参数 4 指定栈上预留容量为 4。
    sv.push_back(element);: 使用 push_back() 方法向 SmallVector 尾部添加元素。
    sv.size(): 返回 SmallVector 中元素的数量。
    sv.capacity(): 返回 SmallVector 的容量,即容器可以容纳的最大元素数量,而无需重新分配内存。
    sv[i]: 使用下标运算符 [] 访问 SmallVector 中的元素。

    通过这个简单的例子,你已经成功地搭建了 Folly 开发环境,并编写了你的第一个 SmallVector 程序。在接下来的章节中,我们将深入学习 SmallVector 的各种功能和用法,探索其更高级的应用场景。

    END_OF_CHAPTER

    2. chapter 2: SmallVector 基础:核心概念与用法(SmallVector Basics: Core Concepts and Usage)

    2.1 模板参数详解:Capacity 与 Size(Template Parameter Details: Capacity and Size)

    Folly::SmallVector 是一个非常有用的容器,它旨在结合 std::arraystd::vector 的优点。为了充分理解 SmallVector 的特性和用法,我们首先需要深入了解其模板参数,特别是 Capacity (容量)Size (大小) 这两个核心概念。

    SmallVector 的模板声明通常如下所示:

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 template <typename T, size_t N, typename Allocator = std::allocator<T>>
    2 class SmallVector;

    这里,我们看到 SmallVector 接受三个模板参数:

    typename T: 这是存储在 SmallVector 中的元素类型。与 std::vector 类似,T 可以是任何可复制或可移动的类型,例如 intfloat、自定义类等。这是容器存储数据的基本单元。

    size_t N: 这是 SmallVector静态容量 (static capacity),也是 SmallVector 最关键的特性之一。N 指定了容器在栈上 (stack) 预留的空间大小,以元素个数为单位。这意味着 SmallVector 会尝试在栈上直接分配最多容纳 NT 类型元素的空间。如果实际存储的元素数量小于等于 N,则所有元素都将存储在栈上,避免了堆内存分配的开销,从而提高了性能。

    typename Allocator = std::allocator<T>: 这是一个可选的模板参数,用于指定内存分配器。默认情况下,使用 std::allocator<T>,即标准的堆内存分配器。你可以自定义分配器来满足特定的内存管理需求,例如使用池分配器或在特定内存区域分配内存。这为高级用户提供了更大的灵活性。

    现在,让我们重点解析 Capacity (容量)Size (大小) 这两个概念,它们是理解 SmallVector 内存布局和性能特性的关键:

    Capacity (容量):在 SmallVector 的上下文中,Capacity 有两种含义,这取决于是否发生了堆内存分配:

    ▮▮▮▮⚝ 静态容量 (Static Capacity):由模板参数 N 决定。这是 SmallVector 在栈上预留的最大元素数量。如果 SmallVector 当前存储的元素数量小于等于 N,那么它的容量就等于 N,所有元素都存储在栈上。
    ▮▮▮▮⚝ 动态容量 (Dynamic Capacity):当需要存储超过 N 个元素时,SmallVector 会像 std::vector 一样,在堆 (heap) 上动态分配内存。此时,动态容量指的是堆上分配的内存可以容纳的元素数量。动态容量通常会大于或等于实际存储的元素数量,以便在后续插入元素时减少内存重新分配的次数。

    Size (大小)Size 指的是 SmallVector 当前实际存储的元素数量。Size 总是小于等于 Capacity。你可以通过 size() 成员函数获取 SmallVector 的大小。

    为了更形象地理解 CapacitySize 的关系,我们可以将 SmallVector 比作一个带有预留座位的容器:

    ⚝ 静态容量 N 就像是容器预先设置好的座位数量,这些座位是固定存在的,无论是否有人坐。
    Size 就像是当前容器中实际坐了多少人。

    内存布局

    SmallVector 存储的元素数量小于等于静态容量 N 时,其内存布局大致如下:

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 [ 栈上预留空间 (大小为 N * sizeof(T)) ]
    2 [ 元素 1 ]
    3 [ 元素 2 ]
    4 [ ... ]
    5 [ 元素 size() ]
    6 [ ... ] (剩余预留空间,未被使用)

    所有元素都存储在栈上预留的空间中,访问速度非常快。

    SmallVector 存储的元素数量超过静态容量 N 时,会发生堆内存分配,其内存布局会变为:

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 [ 栈上小对象 (包含指向堆内存的指针、size、capacity 等信息) ]
    2 [ ... ] (栈上其他数据)
    3 [ 堆上分配的内存块 (大小动态增长) ]
    4 [ 元素 1 ]
    5 [ 元素 2 ]
    6 [ ... ]
    7 [ 元素 size() ]
    8 [ ... ] (堆上剩余空间)

    此时,SmallVector 对象本身(元数据)仍然在栈上,但元素数据存储在堆上动态分配的内存中,这与 std::vector 的内存布局类似。

    动态增长

    当向 SmallVector 中添加元素,且当前 Size 达到 Capacity 时,如果 Capacity 仍然是静态容量 N,则 SmallVector 会执行以下操作以实现动态增长:

    ① 在堆上分配一块更大的内存空间,通常是当前 Size 的倍数(例如,2倍)。
    ② 将栈上存储的元素拷贝移动到堆上新分配的内存中。
    ③ 更新 SmallVector 的内部指针,使其指向堆上新分配的内存。
    ④ 后续的元素将存储在堆上。

    这个动态增长的过程与 std::vectorresize()reserve() 操作类似,涉及到内存分配和元素拷贝/移动的开销。因此,合理选择静态容量 N 非常重要,可以尽量避免或减少堆内存分配和动态增长的发生,从而提升性能。

    总结

    理解 SmallVector 的模板参数 N 以及 CapacitySize 的概念是使用好 SmallVector 的基础。静态容量 N 决定了栈上预留空间的大小,直接影响 SmallVector 在小数据场景下的性能优势。而动态增长机制则保证了 SmallVector 也能像 std::vector 一样处理大数据量的场景。在实际应用中,我们需要根据预期的元素数量和性能需求,合理选择 N 的值,以充分发挥 SmallVector 的优势。

    2.2 构造、析构与赋值:对象的生命周期管理(Construction, Destruction and Assignment: Object Lifecycle Management)

    SmallVector 作为 C++ 中的类,其对象的生命周期管理遵循 C++ 的 RAII (Resource Acquisition Is Initialization) 原则。理解 SmallVector 的构造、析构和赋值行为对于正确使用和避免资源泄漏至关重要。本节将详细解析 SmallVector 的各种构造函数、析构函数以及赋值运算符,并深入探讨拷贝和移动语义在 SmallVector 中的应用。

    构造函数 (Constructors)

    SmallVector 提供了多种构造函数,以满足不同的初始化需求:

    默认构造函数 (Default Constructor)

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 SmallVector<int, 4> sv1; // 静态容量为 4 的 SmallVector,初始大小为 0

    默认构造函数创建一个空的 SmallVector 对象,其大小 (size) 为 0,静态容量 (static capacity) 由模板参数 N 决定(本例中为 4)。此时,SmallVector 内部可能尚未分配任何内存,或者仅仅是在栈上预留了空间。

    填充构造函数 (Fill Constructor)

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 SmallVector<int, 4> sv2(3, 10); // 初始化包含 3 个值为 10 的元素的 SmallVector

    填充构造函数接受两个参数:元素数量和初始值。它会创建包含指定数量、且所有元素都被初始化为给定值的 SmallVector。如果元素数量小于等于静态容量 N,则元素存储在栈上;否则,会发生堆内存分配。

    范围构造函数 (Range Constructor)

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 std::vector<int> vec = {1, 2, 3, 4, 5};
    2 SmallVector<int, 4> sv3(vec.begin(), vec.end()); // 使用迭代器范围初始化

    范围构造函数接受一对迭代器,表示一个元素范围。它会将该范围内的元素拷贝到新的 SmallVector 中。这使得可以使用其他容器(如 std::vectorstd::array 等)或数组来初始化 SmallVector

    拷贝构造函数 (Copy Constructor)

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 SmallVector<int, 4> sv4 = sv3; // 拷贝构造,sv4 是 sv3 的副本
    2 SmallVector<int, 4> sv5(sv3); // 显式拷贝构造

    拷贝构造函数创建一个新的 SmallVector 对象,它是现有 SmallVector 对象(本例中为 sv3)的深拷贝 (deep copy)。这意味着,如果 sv3 的元素存储在栈上,sv4sv5 也会在栈上分配空间并拷贝元素;如果 sv3 的元素存储在堆上,sv4sv5 会在堆上分配新的内存,并将 sv3 堆上的元素拷贝到新的内存中。拷贝构造保证了新对象与原对象在内容上完全独立。

    移动构造函数 (Move Constructor)

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 SmallVector<int, 4> sv6 = std::move(sv3); // 移动构造,sv6 “接管” sv3 的资源
    2 SmallVector<int, 4> sv7(std::move(sv3)); // 显式移动构造

    移动构造函数创建一个新的 SmallVector 对象,它通过移动 (move) 而不是拷贝现有 SmallVector 对象(本例中为 sv3)的资源来初始化。移动构造通常用于提高性能,特别是当源对象是右值 (rvalue) 或即将被销毁时。对于 SmallVector 而言,移动构造主要涉及转移堆内存的所有权(如果存在堆内存分配),以及拷贝栈上的元数据(如 size、capacity 等)。移动构造后,源对象 sv3 将处于有效但未指定 (valid but unspecified) 的状态,通常其大小会变为 0。

    初始化列表构造函数 (Initializer List Constructor)

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 SmallVector<int, 4> sv8 = {1, 2, 3}; // 使用初始化列表初始化
    2 SmallVector<int, 4> sv9{1, 2, 3}; // 统一初始化

    初始化列表构造函数允许使用花括号 {} 包围的初始化列表来创建 SmallVector 对象。这是一种简洁直观的初始化方式,尤其适用于初始化少量元素。

    析构函数 (Destructor)

    SmallVector 的析构函数负责释放对象所占用的资源。其行为取决于 SmallVector 是否在堆上分配了内存:

    如果元素存储在栈上:析构函数主要负责销毁栈上存储的元素对象(如果元素是类对象且有析构函数)。由于栈内存由系统自动管理,因此不需要显式释放栈内存。
    如果元素存储在堆上:析构函数除了销毁元素对象外,还需要释放之前在堆上动态分配的内存。这通常通过调用分配器的 deallocate 函数来完成,以避免内存泄漏。

    无论哪种情况,SmallVector 的析构函数都会确保所有元素被正确销毁,并且所有动态分配的内存都被释放,遵循 RAII 原则。

    赋值运算符 (Assignment Operators)

    SmallVector 提供了拷贝赋值运算符和移动赋值运算符,用于将一个 SmallVector 对象的值赋给另一个已存在的 SmallVector 对象。

    拷贝赋值运算符 (Copy Assignment Operator)

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 SmallVector<int, 4> sv10;
    2 sv10 = sv8; // 拷贝赋值,sv10 变为 sv8 的副本

    拷贝赋值运算符将右侧 SmallVector 对象(本例中为 sv8)的内容拷贝到左侧 SmallVector 对象(本例中为 sv10)。在赋值之前,sv10 原有的元素会被销毁,并释放其占用的内存(如果需要)。然后,根据 sv8 的内存布局,sv10 会在栈上或堆上分配新的内存,并将 sv8 的元素拷贝到新的内存中。与拷贝构造函数类似,拷贝赋值运算符也执行深拷贝。

    移动赋值运算符 (Move Assignment Operator)

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 SmallVector<int, 4> sv11;
    2 sv11 = std::move(sv8); // 移动赋值,sv11 “接管” sv8 的资源

    移动赋值运算符将右侧 SmallVector 对象(本例中为 sv8)的资源移动到左侧 SmallVector 对象(本例中为 sv11)。与移动构造函数类似,移动赋值运算符主要涉及转移堆内存的所有权(如果存在堆内存分配),以及拷贝栈上的元数据。在赋值之前,sv11 原有的元素会被销毁,并释放其占用的内存(如果需要)。移动赋值通常比拷贝赋值更高效,因为它避免了元素的拷贝操作。移动赋值后,源对象 sv8 将处于有效但未指定的状态。

    总结

    SmallVector 提供了丰富的构造函数和赋值运算符,支持默认构造、填充初始化、范围初始化、拷贝和移动语义以及初始化列表。理解这些构造和赋值操作的行为,特别是拷贝和移动语义的区别,对于编写高效且资源管理正确的 C++ 代码至关重要。析构函数则负责在 SmallVector 对象生命周期结束时,释放其占用的资源,确保内存安全。在实际编程中,应根据具体需求选择合适的构造和赋值方式,以优化性能并避免不必要的资源开销。

    2.3 元素访问与迭代器(Element Access and Iterators)

    访问 SmallVector 中的元素以及遍历容器是使用 SmallVector 的基本操作。SmallVector 提供了多种方式来访问和操作容器内的元素,包括直接访问、边界检查访问以及迭代器访问。本节将详细介绍 SmallVector 提供的元素访问方法和迭代器,并演示它们的使用方式。

    元素访问 (Element Access)

    SmallVector 提供了以下成员函数来直接访问容器中的元素:

    operator[]:下标运算符,提供无边界检查 (no bounds checking) 的元素访问。

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 SmallVector<int, 4> sv = {10, 20, 30};
    2 int firstElement = sv[0]; // 访问第一个元素,firstElement = 10
    3 sv[1] = 25; // 修改第二个元素的值,sv 变为 {10, 25, 30}

    operator[] 允许使用下标索引来访问 SmallVector 中的元素,类似于访问数组。重要提示operator[] 不进行边界检查。如果访问的索引超出有效范围(即小于 0 或大于等于 size()),则行为是未定义的 (undefined behavior),可能导致程序崩溃或产生不可预测的结果。因此,在使用 operator[] 时,务必确保索引的有效性。

    at(size_type pos):提供有边界检查 (bounds checking) 的元素访问。

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 SmallVector<int, 4> sv = {10, 20, 30};
    2 int secondElement = sv.at(1); // 访问第二个元素,secondElement = 20
    3
    4 try {
    5 int element = sv.at(3); // 尝试访问索引为 3 的元素(超出范围)
    6 } catch (const std::out_of_range& e) {
    7 std::cerr << "Out of range access: " << e.what() << std::endl; // 捕获异常
    8 }

    at() 成员函数也用于访问指定索引位置的元素,但与 operator[] 不同的是,at() 会进行边界检查。如果访问的索引 pos 超出有效范围(即 pos >= size()),at() 会抛出一个 std::out_of_range 异常。这使得 at() 更加安全,尤其是在不确定索引是否有效的情况下。使用 try-catch 块可以捕获并处理越界访问异常。

    front():访问第一个元素。

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 SmallVector<int, 4> sv = {10, 20, 30};
    2 int first = sv.front(); // 访问第一个元素,first = 10

    front() 成员函数返回 SmallVector 中第一个元素的引用 (reference)前提条件SmallVector 必须非空 (not empty),即 size() > 0。如果对空 SmallVector 调用 front(),行为是未定义的。

    back():访问最后一个元素。

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 SmallVector<int, 4> sv = {10, 20, 30};
    2 int last = sv.back(); // 访问最后一个元素,last = 30

    back() 成员函数返回 SmallVector 中最后一个元素的引用前提条件SmallVector 必须非空。如果对空 SmallVector 调用 back(),行为是未定义的。

    迭代器 (Iterators)

    迭代器是 C++ 中用于遍历容器元素的通用机制。SmallVector 提供了多种迭代器类型,以支持不同方向和访问方式的遍历。

    begin()end():返回指向容器首元素和尾元素之后位置的迭代器 (iterator)

    begin() 返回指向 SmallVector 第一个元素的迭代器。
    end() 返回指向 SmallVector 尾元素之后位置的迭代器,即past-the-end iterator。它不指向任何实际元素,而是作为遍历结束的标志。

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 SmallVector<int, 4> sv = {10, 20, 30};
    2 for (auto it = sv.begin(); it != sv.end(); ++it) {
    3 std::cout << *it << " "; // 输出:10 20 30
    4 }
    5 std::cout << std::endl;

    上述代码使用 begin()end() 返回的迭代器遍历 SmallVector 中的所有元素。auto it = sv.begin() 初始化迭代器 it 指向第一个元素,it != sv.end() 判断是否到达容器末尾,++it 将迭代器移动到下一个元素,*it 解引用迭代器以访问当前元素的值。

    cbegin()cend():返回指向容器首元素和尾元素之后位置的常量迭代器 (const_iterator)

    cbegin()cend()begin()end() 类似,但它们返回的是常量迭代器。常量迭代器只能用于读取元素,而不能修改元素的值。这在需要只读访问容器元素时非常有用。

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 SmallVector<int, 4> sv = {10, 20, 30};
    2 for (auto it = sv.cbegin(); it != sv.cend(); ++it) {
    3 std::cout << *it << " "; // 输出:10 20 30 (只读访问)
    4 // *it = 40; // 错误:常量迭代器不能修改元素
    5 }
    6 std::cout << std::endl;

    rbegin()rend():返回指向容器尾元素和首元素之前位置的反向迭代器 (reverse_iterator)

    rbegin() 返回指向 SmallVector 最后一个元素的反向迭代器。
    rend() 返回指向 SmallVector 首元素之前位置的反向迭代器,作为反向遍历结束的标志。

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 SmallVector<int, 4> sv = {10, 20, 30};
    2 for (auto rit = sv.rbegin(); rit != sv.rend(); ++rit) {
    3 std::cout << *rit << " "; // 输出:30 20 10 (反向遍历)
    4 }
    5 std::cout << std::endl;

    上述代码使用 rbegin()rend() 返回的反向迭代器反向遍历 SmallVector 中的元素。

    crbegin()crend():返回指向容器尾元素和首元素之前位置的常量反向迭代器 (const_reverse_iterator)

    crbegin()crend()rbegin()rend() 类似,但它们返回的是常量反向迭代器,用于只读反向遍历。

    迭代器失效 (Iterator Invalidation)

    在使用迭代器时,需要注意迭代器失效的问题。某些容器操作可能会导致迭代器失效,即迭代器不再指向有效的元素位置。对于 SmallVector 而言,以下操作可能会导致迭代器失效:

    插入元素 (insert, emplace_back, push_back 等):如果在静态容量范围内插入元素,栈上元素的迭代器通常不会失效。但如果插入元素导致堆内存分配或重新分配,则所有迭代器都可能失效。
    删除元素 (erase, pop_back, clear 等):删除元素会导致被删除元素之后的所有元素的迭代器失效。
    调整大小 (resize, reserve, shrink_to_fit 等):调整容器大小可能会导致内存重新分配,从而使所有迭代器失效。

    因此,在进行容器操作后,如果之前获取的迭代器可能受到影响,应重新获取迭代器,以确保迭代器的有效性。

    总结

    SmallVector 提供了多种灵活的元素访问方式,包括 operator[]at()front()back() 以及各种迭代器。operator[] 提供快速的无边界检查访问,at() 提供安全的有边界检查访问,front()back() 访问首尾元素,而迭代器则支持各种遍历方式。在选择元素访问方式时,需要根据具体场景权衡性能和安全性。同时,要时刻注意迭代器失效的问题,避免因使用失效迭代器而导致程序错误。熟练掌握这些元素访问和迭代器方法,可以有效地操作和遍历 SmallVector 中的数据。

    2.4 容量管理与元素操作(Capacity Management and Element Operations)

    SmallVector 提供了丰富的成员函数来管理容器的容量和操作容器中的元素。这些函数使得我们可以动态地调整 SmallVector 的大小、预留空间、添加、删除和修改元素。本节将详细介绍 SmallVector 的容量管理和元素操作相关的常用 API。

    容量管理 (Capacity Management)

    以下成员函数用于查询和管理 SmallVector 的容量:

    size():返回 SmallVector 中当前元素的数量,即 Size (大小)

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 SmallVector<int, 4> sv = {10, 20, 30};
    2 size_t currentSize = sv.size(); // currentSize = 3

    size() 函数返回一个 size_t 类型的值,表示 SmallVector 当前存储的元素个数。

    capacity():返回 SmallVector 的容量,即在不重新分配内存的情况下可以容纳的最大元素数量,即 Capacity (容量)

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 SmallVector<int, 4> sv = {10, 20, 30};
    2 size_t currentCapacity = sv.capacity(); // currentCapacity >= 3, 可能是 4 或更大

    capacity() 函数返回一个 size_t 类型的值,表示 SmallVector 当前分配的内存空间可以容纳的元素个数。对于静态容量 NSmallVector,在未发生堆分配前,capacity() 通常等于 N。当发生堆分配后,capacity() 可能大于 N,并且会随着动态增长而变化。

    empty():检查 SmallVector 是否为空,即 size() 是否为 0。

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 SmallVector<int, 4> sv1;
    2 bool isEmpty1 = sv1.empty(); // isEmpty1 = true
    3
    4 SmallVector<int, 4> sv2 = {10, 20};
    5 bool isEmpty2 = sv2.empty(); // isEmpty2 = false

    empty() 函数返回一个 bool 值,如果 SmallVector 为空(不包含任何元素),则返回 true,否则返回 false

    reserve(size_type n):预留至少能容纳 n 个元素的空间。

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 SmallVector<int, 4> sv;
    2 sv.reserve(10); // 预留至少能容纳 10 个元素的空间
    3 size_t reservedCapacity = sv.capacity(); // reservedCapacity >= 10

    reserve(n) 函数用于预先分配至少能容纳 n 个元素的内存空间。如果 n 大于当前 capacity()reserve() 会分配新的内存空间,并将现有元素移动到新的内存中。如果 n 小于或等于当前 capacity()reserve() 可能不做任何操作。reserve() 不会改变 size(),它只影响 capacity(),用于提前分配内存,以避免后续插入元素时频繁的内存重新分配,从而提高性能。

    shrink_to_fit():尝试释放多余的容量,将容量调整为与大小匹配。

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 SmallVector<int, 4> sv;
    2 sv.reserve(100); // 预留 100 个元素的空间
    3 sv.push_back(1);
    4 sv.push_back(2);
    5 sv.shrink_to_fit(); // 尝试释放多余容量
    6 size_t currentCapacity = sv.capacity(); // currentCapacity 可能接近 2

    shrink_to_fit() 函数尝试释放 SmallVector 中多余的容量,将 capacity() 减小到与 size() 匹配或接近的水平。但需要注意的是,shrink_to_fit() 只是一个请求 (request),具体的实现可能并不保证一定能释放内存,或者释放多少内存取决于具体的分配器实现。shrink_to_fit() 主要用于在确定不再需要大量额外空间时,减少内存占用

    元素操作 (Element Operations)

    以下成员函数用于添加、删除和修改 SmallVector 中的元素:

    push_back(const T& value)push_back(T&& value):在 SmallVector 的末尾添加一个元素。

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 SmallVector<int, 4> sv;
    2 sv.push_back(10); // 添加元素 10
    3 sv.push_back(20); // 添加元素 20

    push_back() 有两个重载版本,分别接受左值引用和右值引用。它在 SmallVector 的末尾添加一个新元素。如果当前 size() 达到 capacity(),且容量是静态容量 N,则可能触发堆内存分配和动态增长。

    emplace_back(Args&&... args):在 SmallVector 的末尾就地构造 (in-place construct) 一个元素。

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 SmallVector<std::pair<int, std::string>, 4> sv_pairs;
    2 sv_pairs.emplace_back(1, "apple"); // 就地构造一个 pair<int, string> 对象

    emplace_back()push_back() 类似,也在末尾添加元素,但 emplace_back() 接受可变参数,用于直接在容器内部构造元素对象,而不是先创建临时对象再拷贝或移动。这可以避免不必要的拷贝或移动操作,尤其对于构造复杂对象时,emplace_back() 通常更高效。

    pop_back():移除 SmallVector 的最后一个元素。

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 SmallVector<int, 4> sv = {10, 20, 30};
    2 sv.pop_back(); // 移除最后一个元素,sv 变为 {10, 20}

    pop_back() 函数移除 SmallVector 的最后一个元素,size() 减 1。前提条件SmallVector 必须非空

    insert(iterator pos, const T& value)insert(iterator pos, T&& value):在指定位置 pos 插入一个元素。

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 SmallVector<int, 4> sv = {10, 30};
    2 auto it = sv.begin() + 1; // 指向第二个元素的位置
    3 sv.insert(it, 20); // 在第二个元素位置插入 20,sv 变为 {10, 20, 30}

    insert() 函数在迭代器 pos 指向的位置之前插入一个新元素。插入位置之后的所有元素需要向后移动。insert() 也有左值引用和右值引用两个重载版本。插入元素可能导致内存重新分配和迭代器失效。

    erase(iterator pos)erase(iterator first, iterator last):移除指定位置或范围的元素。

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 SmallVector<int, 4> sv = {10, 20, 30, 40};
    2 auto it1 = sv.begin() + 1;
    3 sv.erase(it1); // 移除第二个元素 (20),sv 变为 {10, 30, 40}
    4
    5 auto it2 = sv.begin() + 1;
    6 auto it3 = sv.begin() + 3;
    7 sv.erase(it2, it3); // 移除范围 [it2, it3) 的元素 (30),sv 变为 {10, 40}

    erase() 函数可以移除迭代器 pos 指向的单个元素,或者移除迭代器范围 [first, last) 内的元素。移除元素后,后续元素需要向前移动。erase() 操作可能导致迭代器失效。

    clear():移除 SmallVector 中的所有元素,使其变为空容器。

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 SmallVector<int, 4> sv = {10, 20, 30};
    2 sv.clear(); // 移除所有元素,sv 变为空容器,size() = 0

    clear() 函数移除 SmallVector 中的所有元素,但不会释放已分配的内存空间,capacity() 保持不变。

    总结

    SmallVector 提供了丰富的容量管理和元素操作 API,包括查询大小和容量、预留空间、释放多余容量、添加元素(push_back, emplace_back)、删除元素(pop_back, erase, clear)以及插入元素(insert)。熟练掌握这些 API,可以灵活地控制 SmallVector 的内存使用和元素内容。在实际应用中,合理使用 reserve() 预留空间可以减少动态增长的开销,shrink_to_fit() 可以减少内存占用,emplace_back() 可以提高元素构造效率,而 erase()clear() 则用于元素的删除和容器的清空。

    2.4.1 实战代码:动态数组的实现(Practical Code: Implementation of Dynamic Array)

    为了更好地理解 SmallVector 的应用,并演示如何使用其容量管理和元素操作 API,我们来实现一个简单的动态数组 (Dynamic Array) 类,名为 DynamicArray,其内部使用 Folly::SmallVector 作为底层存储。DynamicArray 类将提供类似于 std::vector 的基本功能,例如添加元素、访问元素、获取大小等。

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 #include <folly/container/SmallVector.h>
    2 #include <stdexcept> // for std::out_of_range
    3 #include <iostream> // for std::cout
    4
    5 template <typename T, size_t N>
    6 class DynamicArray {
    7 private:
    8 folly::SmallVector<T, N> data_; // 使用 SmallVector 作为底层存储
    9
    10 public:
    11 DynamicArray() = default; // 默认构造函数
    12
    13 // 添加元素到末尾
    14 void push_back(const T& value) {
    15 data_.push_back(value);
    16 }
    17
    18 void push_back(T&& value) {
    19 data_.push_back(std::move(value));
    20 }
    21
    22 // 获取元素数量
    23 size_t size() const {
    24 return data_.size();
    25 }
    26
    27 // 检查是否为空
    28 bool empty() const {
    29 return data_.empty();
    30 }
    31
    32 // 访问元素 (无边界检查)
    33 T& operator[](size_t index) {
    34 return data_[index];
    35 }
    36
    37 const T& operator[](size_t index) const {
    38 return data_[index];
    39 }
    40
    41 // 访问元素 (有边界检查)
    42 T& at(size_t index) {
    43 return data_.at(index);
    44 }
    45
    46 const T& at(size_t index) const {
    47 return data_.at(index);
    48 }
    49
    50 // 清空数组
    51 void clear() {
    52 data_.clear();
    53 }
    54 };
    55
    56 int main() {
    57 // 创建一个静态容量为 4 的 DynamicArray<int, 4> 对象
    58 DynamicArray<int, 4> arr;
    59
    60 // 添加元素
    61 arr.push_back(10);
    62 arr.push_back(20);
    63 arr.push_back(30);
    64 arr.push_back(40);
    65 arr.push_back(50); // 超过静态容量,会发生堆分配
    66
    67 // 访问元素
    68 std::cout << "Size: " << arr.size() << std::endl; // 输出 Size: 5
    69 std::cout << "Element at index 2: " << arr[2] << std::endl; // 输出 Element at index 2: 30
    70 std::cout << "Element at index 4: " << arr.at(4) << std::endl; // 输出 Element at index 4: 50
    71
    72 // 遍历元素
    73 std::cout << "Elements: ";
    74 for (size_t i = 0; i < arr.size(); ++i) {
    75 std::cout << arr[i] << " "; // 输出 Elements: 10 20 30 40 50
    76 }
    77 std::cout << std::endl;
    78
    79 // 清空数组
    80 arr.clear();
    81 std::cout << "Size after clear: " << arr.size() << std::endl; // 输出 Size after clear: 0
    82 std::cout << "Is empty: " << (arr.empty() ? "Yes" : "No") << std::endl; // 输出 Is empty: Yes
    83
    84 return 0;
    85 }

    代码解析

    模板参数DynamicArray 类也使用了模板,接受两个参数:typename T 表示元素类型,size_t N 表示 SmallVector 的静态容量。

    底层存储DynamicArray 的私有成员 data_ 是一个 folly::SmallVector<T, N> 对象,用于实际存储元素。

    公共接口DynamicArray 提供了 push_back()size()empty()operator[]at()clear() 等公共成员函数,这些函数直接委托 (delegate) 调用 data_ 对象的相应成员函数。例如,DynamicArray::push_back() 内部直接调用 data_.push_back()

    边界检查DynamicArray 同时提供了无边界检查的 operator[] 和有边界检查的 at() 两种元素访问方式,与 SmallVector 保持一致。

    main() 函数示例main() 函数演示了 DynamicArray 的基本用法,包括创建对象、添加元素、访问元素、遍历元素和清空数组。可以看到,DynamicArray 的使用方式与 std::vector 非常相似,但底层使用了 SmallVector,从而可能在小数据场景下获得性能优势。

    总结

    通过实现 DynamicArray 类,我们展示了如何使用 SmallVector 作为底层容器来构建自定义的数据结构。DynamicArray 类封装了 SmallVector 的基本功能,并提供了类似于 std::vector 的接口。这个实战代码示例帮助我们更好地理解 SmallVector 的应用,以及如何利用其容量管理和元素操作 API 来实现动态数组的功能。在实际项目中,可以根据具体需求,基于 SmallVector 构建更复杂、更高效的数据结构。

    2.4.2 最佳实践:选择合适的初始容量(Best Practice: Choosing the Right Initial Capacity)

    SmallVector 的一个关键特性是其静态容量 N,它决定了在栈上预留的空间大小。合理选择 N 的值对于充分发挥 SmallVector 的性能优势至关重要。如果 N 选择得当,可以最大限度地利用栈内存,避免堆内存分配,从而提高性能。反之,如果 N 选择不当,可能会导致栈空间浪费或频繁的堆内存分配,降低性能。本节将探讨如何根据不同的应用场景,选择合适的 SmallVector 静态容量 N,并提供一些最佳实践建议。

    影响因素

    选择合适的静态容量 N 需要考虑以下几个关键因素:

    预期的元素数量:这是最主要的考虑因素。你需要预估 (estimate) SmallVector 在大多数情况下需要存储的元素数量范围。如果你的应用场景中,SmallVector 经常存储少量元素(例如,几个到几十个),那么设置一个较小的 N 值(例如,4、8、16、32)可能就足够了,这样可以充分利用栈内存的优势。如果元素数量波动较大,或者经常需要存储大量元素,那么可能需要权衡栈内存的使用和堆内存分配的开销。

    栈内存限制:栈内存通常比堆内存小,且栈空间是有限的。过大的静态容量 N 会占用较多的栈空间,可能导致栈溢出 (stack overflow) 的风险,尤其是在递归调用或多线程环境下。因此,需要根据目标平台的栈内存大小限制,合理设置 N 的上限。通常,对于嵌入式系统或资源受限的环境,更需要谨慎控制栈内存的使用。

    元素类型的大小:元素类型 T 的大小也会影响栈内存的占用。如果 T 是一个较大的对象(例如,包含大量成员变量的类对象),那么即使 N 值较小,总的栈内存占用也可能比较大。反之,如果 T 是基本类型(如 intfloat)或小型对象,则可以适当增大 N 值。

    性能需求SmallVector 的主要优势在于小数据场景下的性能。如果你的应用对性能要求非常高,尤其是在元素数量较少的情况下,那么应该尽量选择合适的 N 值,以避免堆内存分配,最大化性能提升。如果性能不是首要考虑因素,或者主要处理大数据量,那么 N 的选择可以相对宽松一些。

    最佳实践建议

    基于以上影响因素,以下是一些选择 SmallVector 静态容量 N 的最佳实践建议:

    经验法则:对于大多数通用场景,可以考虑将 N 设置为 8、16 或 32。这些值通常能在栈内存占用和性能提升之间取得较好的平衡。例如,如果你的 SmallVector 主要用于存储少量配置参数、临时数据或小型数据结构,这些值通常是合适的。

    基于统计分析:如果你的应用场景比较明确,可以进行统计分析 (statistical analysis),收集 SmallVector 实际存储元素数量的分布情况。例如,可以记录 SmallVectorsize() 值的最大值、平均值、中位数等统计指标。根据这些统计数据,选择一个略大于平均值或中位数N 值,可以在大多数情况下避免堆内存分配,同时又不会过度占用栈空间。

    动态调整策略:在某些情况下,静态容量 N 可能无法满足所有场景的需求。可以考虑使用动态调整策略 (dynamic adjustment strategy)。例如,可以根据运行时的实际情况,动态地选择不同的 SmallVector 类型,或者在必要时切换到 std::vector。但这会增加代码的复杂性,需要权衡利弊。

    基准测试:对于性能敏感的应用,基准测试 (benchmark) 是必不可少的。可以针对不同的 N 值进行性能测试,例如使用 Google Benchmark 等工具,测量不同 N 值下的性能表现(例如,插入、访问、遍历等操作的耗时)。通过基准测试,可以找到在特定场景下性能最优的 N 值。

    考虑元素类型:在选择 N 值时,务必考虑元素类型 T 的大小。如果 sizeof(T) 较大,则应适当减小 N 值,以控制栈内存占用。反之,如果 sizeof(T) 较小,则可以适当增大 N 值。

    示例

    假设你正在开发一个网络服务器,需要处理客户端请求。每个请求可能包含少量参数,例如请求类型、请求 ID、时间戳等,这些参数可以存储在一个 SmallVector<std::string, N> 中。

    ⚝ 如果你预估大多数请求的参数数量在 5 个以内,可以将 N 设置为 8 或 16。
    ⚝ 如果你发现某些请求的参数数量可能会超过 32 个,但这种情况比较少见,可以考虑将 N 设置为 32,并在代码中处理超过静态容量的情况(例如,记录日志或使用 std::vector 替代)。
    ⚝ 如果你对性能要求非常高,可以进行基准测试,比较 N 为 8、16、32 等不同值时的请求处理速度,选择性能最优的 N 值。

    总结

    选择合适的 SmallVector 静态容量 N 是一个需要权衡的过程,没有一个通用的最佳值,最佳的 N 值取决于具体的应用场景、预期的元素数量、栈内存限制、元素类型大小和性能需求等因素。通过合理的预估、统计分析、基准测试和动态调整策略,可以找到最适合特定场景的 N 值,充分发挥 SmallVector 的性能优势,并避免潜在的栈溢出风险。记住,没有银弹 (no silver bullet),最佳实践是根据实际情况进行权衡和选择。

    2.5 类型安全与异常处理(Type Safety and Exception Handling)

    SmallVector 作为 C++ 模板库的一部分,继承了 C++ 的类型安全 (type safety)异常处理 (exception handling) 机制。理解 SmallVector 的类型安全特性以及异常安全编程实践,有助于编写健壮、可靠的 C++ 代码。本节将探讨 SmallVector 的类型安全性,以及在使用 SmallVector 时如何进行异常处理,以确保程序的正确性和稳定性。

    类型安全 (Type Safety)

    SmallVector 的类型安全性主要体现在以下几个方面:

    模板参数化SmallVector 是一个模板类,通过模板参数 typename T 指定存储的元素类型。这意味着在编译时,SmallVector 只能存储指定类型的元素。如果尝试将其他类型的元素添加到 SmallVector 中,编译器会报错,从而在编译阶段就避免了类型错误。

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 SmallVector<int, 4> sv_int;
    2 sv_int.push_back(10); // 正确:添加 int 类型元素
    3 // sv_int.push_back("hello"); // 编译错误:类型不匹配,不能添加 string 类型元素
    4
    5 SmallVector<std::string, 4> sv_str;
    6 sv_str.push_back("world"); // 正确:添加 string 类型元素

    强类型接口SmallVector 的成员函数,例如 push_back()insert()assign() 等,都要求传入的参数类型与 SmallVector 存储的元素类型 T 兼容。这进一步增强了类型安全性,避免了因类型不匹配导致的运行时错误。

    迭代器类型SmallVector 的迭代器也是类型安全的。例如,SmallVector<int, 4>::iterator 只能用于遍历 SmallVector<int, 4> 类型的容器,并且解引用迭代器会得到 int& 类型的引用。这保证了迭代器操作的类型安全性。

    编译时检查:C++ 编译器会在编译时进行严格的类型检查,包括模板类型推导、函数参数类型匹配、迭代器类型检查等。这使得大部分类型错误可以在编译阶段被发现,而不是等到运行时才暴露出来,从而提高了代码的可靠性。

    异常处理 (Exception Handling)

    SmallVector 的设计考虑了异常安全,其大部分操作都提供了较强的异常安全保证。异常安全是指在程序发生异常时,程序仍然能够保持在一种有效 (valid) 的状态,不会发生资源泄漏或数据损坏。C++ 中通常将异常安全分为三个级别:

    不提供异常安全保证 (No exception safety):如果操作抛出异常,程序状态可能处于未知或不一致的状态,可能导致资源泄漏或数据损坏。
    基本异常安全保证 (Basic exception safety):如果操作抛出异常,程序状态不会损坏,所有对象仍然处于有效状态,不会发生资源泄漏。但程序状态可能与操作开始前不同。
    强异常安全保证 (Strong exception safety):如果操作抛出异常,程序状态保持不变,就像操作从未发生过一样。如果操作成功完成,则保证操作的完整性。

    SmallVector 的大部分操作都提供了基本异常安全保证,部分操作甚至提供了强异常安全保证。以下是一些常见的 SmallVector 操作的异常安全级别:

    构造函数SmallVector 的拷贝构造函数和移动构造函数通常提供强异常安全保证。如果构造过程中抛出异常,源对象的状态保持不变。

    赋值运算符SmallVector 的拷贝赋值运算符和移动赋值运算符通常提供基本异常安全保证。如果赋值过程中抛出异常,目标对象可能处于有效但未指定的状态,但不会发生资源泄漏。

    push_back()emplace_back():这两个函数在插入元素时,如果需要动态分配内存,可能会抛出 std::bad_alloc 异常(内存分配失败)。它们通常提供基本异常安全保证。如果抛出异常,SmallVector 的状态仍然有效,但新元素可能没有被成功插入。

    insert()emplace():与 push_back()emplace_back() 类似,insert()emplace() 在插入元素时也可能抛出 std::bad_alloc 异常。它们通常也提供基本异常安全保证

    erase()pop_back():这两个函数通常不会抛出异常,除非元素类型的拷贝/移动构造函数或析构函数抛出异常(如果元素是类对象)。在元素操作本身层面,它们是异常安全的。

    at()at() 函数在索引越界时会抛出 std::out_of_range 异常。这是一种逻辑错误 (logic error) 导致的异常,用于指示程序错误使用了 at() 函数。

    reserve()shrink_to_fit()reserve() 在内存分配失败时可能抛出 std::bad_alloc 异常。shrink_to_fit() 通常不会抛出异常。

    异常处理实践

    在使用 SmallVector 进行异常处理时,可以遵循以下实践:

    使用 at() 进行边界检查:当你需要访问 SmallVector 中的元素,并且不确定索引是否有效时,应该使用 at() 函数,而不是 operator[]at() 会进行边界检查,并在越界时抛出 std::out_of_range 异常,你可以使用 try-catch 块捕获并处理这个异常。

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 SmallVector<int, 4> sv = {10, 20, 30};
    2 try {
    3 int element = sv.at(5); // 索引越界,抛出 std::out_of_range 异常
    4 } catch (const std::out_of_range& e) {
    5 std::cerr << "Index out of range: " << e.what() << std::endl;
    6 // 处理越界异常的逻辑
    7 }

    处理内存分配异常:当使用 push_back()emplace_back()insert()emplace()reserve() 等可能导致内存分配的操作时,应该考虑捕获 std::bad_alloc 异常,并进行适当的处理,例如释放已分配的资源、记录错误日志、或者优雅地终止程序。

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 SmallVector<int, 4> sv;
    2 try {
    3 for (int i = 0; i < 1000000; ++i) {
    4 sv.push_back(i); // 可能抛出 std::bad_alloc 异常
    5 }
    6 } catch (const std::bad_alloc& e) {
    7 std::cerr << "Memory allocation failed: " << e.what() << std::endl;
    8 // 处理内存分配失败的逻辑
    9 }

    RAII 和异常安全资源管理:利用 RAII (Resource Acquisition Is Initialization) 原则,将资源封装在类中,并在类的构造函数中获取资源,析构函数中释放资源。这样可以确保在发生异常时,资源能够被正确释放,避免资源泄漏。SmallVector 本身就是一个 RAII 容器,它在析构函数中负责释放其管理的内存。

    避免在析构函数中抛出异常:C++ 规范建议不要在析构函数中抛出异常。如果析构函数中可能发生错误,应该在析构函数外部进行处理,或者设计成在析构函数中忽略错误。

    总结

    SmallVector 提供了良好的类型安全性和基本异常安全保证。类型安全特性通过模板参数化和强类型接口在编译时避免类型错误。异常安全保证确保在发生异常时,程序状态仍然有效,不会发生资源泄漏。在使用 SmallVector 时,应该遵循异常处理的最佳实践,例如使用 at() 进行边界检查、处理内存分配异常、利用 RAII 进行资源管理等,以编写健壮、可靠的 C++ 代码。理解 SmallVector 的类型安全和异常处理特性,可以帮助开发者更好地利用 SmallVector,并避免潜在的错误和风险。

    END_OF_CHAPTER

    3. chapter 3: SmallVector 进阶:深入原理与高级特性(SmallVector Advanced: In-depth Principles and Advanced Features)

    3.1 内存分配策略:栈上空间与堆上空间(Memory Allocation Strategy: Stack Space and Heap Space):SmallVector 的内存管理机制深度剖析

    SmallVector 最核心的特性之一,也是它区别于 std::vector 等动态数组容器的关键所在,就是其独特的内存分配策略。理解 SmallVector 的内存管理机制,是深入掌握其性能优势和适用场景的基础。本节将深入剖析 SmallVector 如何巧妙地利用栈上空间和堆上空间,实现高效的内存管理。

    知识框架:

    栈上空间(Stack Space)
    ▮▮▮▮ⓑ 概念:栈是一种后进先出(LIFO, Last-In-First-Out)的数据结构,由编译器自动管理,用于存储函数调用时的局部变量、函数参数、返回地址等。栈内存的分配和释放速度非常快,效率高。
    ▮▮▮▮ⓒ 特点:
    ▮▮▮▮▮▮▮▮❹ 自动管理:由编译器自动分配和释放,无需手动干预。
    ▮▮▮▮▮▮▮▮❺ 快速分配与释放:栈操作速度极快,效率高。
    ▮▮▮▮▮▮▮▮❻ 大小限制:栈空间通常有限,容易发生栈溢出(Stack Overflow)错误。
    ▮▮▮▮ⓖ SmallVector 的栈上使用:SmallVector 利用模板参数 N 指定的固定大小的栈上数组 buf_ 来存储元素。当元素数量小于等于 N 时,所有元素都存储在栈上,避免了堆内存分配的开销。

    堆上空间(Heap Space)
    ▮▮▮▮ⓑ 概念:堆是用于动态内存分配的区域,由程序员手动管理。使用 newmalloc 等操作在堆上分配内存,使用 deletefree 等操作释放内存。
    ▮▮▮▮ⓒ 特点:
    ▮▮▮▮▮▮▮▮❹ 手动管理:需要程序员显式地分配和释放内存,容易出现内存泄漏(Memory Leak)和悬 dangling 指针等问题。
    ▮▮▮▮▮▮▮▮❺ 灵活分配:堆空间较大,可以动态分配任意大小的内存。
    ▮▮▮▮▮▮▮▮❻ 分配与释放速度相对较慢:堆操作涉及复杂的内存管理算法,速度比栈操作慢。
    ▮▮▮▮ⓖ SmallVector 的堆上使用:当 SmallVector 存储的元素数量超过栈上空间 N 的限制时,它会像 std::vector 一样,在堆上动态分配内存来存储元素。

    SmallVector 的内存管理机制
    ▮▮▮▮ⓑ 混合策略:SmallVector 采用栈上空间优先,堆上空间备用的混合内存分配策略。
    ▮▮▮▮ⓒ 阈值 N:模板参数 N 是一个关键的阈值。当元素数量小于等于 N 时,使用栈上 buf_;当元素数量超过 N 时,动态分配堆内存。
    ▮▮▮▮ⓓ 动态扩容:当栈上空间不足且需要继续添加元素时,SmallVector 会自动将数据迁移到堆上,并进行动态扩容,类似于 std::vector 的扩容机制。
    ▮▮▮▮ⓔ 内存释放:当 SmallVector 对象销毁时,如果数据存储在堆上,则会释放堆内存;如果数据存储在栈上,则栈内存由编译器自动释放。

    实战代码:

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 #include <folly/container/SmallVector.h>
    2 #include <iostream>
    3
    4 int main() {
    5 // 容量为 4 的 SmallVector
    6 folly::SmallVector<int, 4> sv;
    7
    8 std::cout << "Initial capacity: " << sv.capacity() << std::endl; // 输出 4,栈上容量
    9
    10 // 添加 4 个元素,仍在栈上
    11 for (int i = 0; i < 4; ++i) {
    12 sv.push_back(i);
    13 std::cout << "Size after push_back(" << i << "): " << sv.size() << ", capacity: " << sv.capacity() << std::endl;
    14 }
    15 std::cout << "Capacity after adding 4 elements: " << sv.capacity() << std::endl; // 仍然输出 4
    16
    17 // 添加第 5 个元素,触发堆内存分配
    18 sv.push_back(4);
    19 std::cout << "Size after push_back(4): " << sv.size() << ", capacity: " << sv.capacity() << std::endl; // capacity 变为大于 4 的值,例如 6,堆上容量
    20
    21 return 0;
    22 }

    代码解析:

    folly::SmallVector<int, 4> sv;:声明一个 SmallVector,其栈上容量 N 为 4,元素类型为 int
    ⚝ 初始 capacity() 为 4,表明初始状态下,SmallVector 使用栈上空间。
    ⚝ 前 4 次 push_back() 操作,size() 增加,但 capacity() 保持不变,说明元素存储在栈上。
    ⚝ 第 5 次 push_back(4) 操作后,capacity() 显著增加,表明触发了堆内存分配,SmallVector 将数据迁移到了堆上,并进行了扩容。

    高级应用:

    性能优化:对于已知元素数量通常较小且不超过栈上容量 N 的场景,使用 SmallVector 可以显著减少堆内存分配和释放的开销,提高程序性能,尤其是在对性能要求极高的场景,如嵌入式系统、实时系统、游戏开发等。
    减少内存碎片:由于栈内存的分配和释放是连续的,使用栈上空间可以减少堆内存碎片,提高内存利用率。
    避免堆分配失败:在内存资源受限的环境下,栈内存分配的成功率通常高于堆内存,使用 SmallVector 可以降低因堆分配失败而导致程序崩溃的风险。

    API 全面解析:

    SmallVector<T, N>:模板类,T 是元素类型,N 是栈上容量。
    capacity():返回当前 SmallVector 的容量。当数据存储在栈上时,返回栈上容量 N;当数据存储在堆上时,返回堆上已分配的容量。
    size():返回当前 SmallVector 中元素的数量。

    总结:

    SmallVector 的内存分配策略是其核心优势所在。通过巧妙地结合栈上空间和堆上空间,SmallVector 在保证动态数组功能的同时,尽可能地利用栈内存的快速分配和释放特性,从而在特定场景下实现更高的性能。理解 SmallVector 的内存管理机制,有助于我们更好地选择合适的容器,并充分发挥 SmallVector 的优势。


    3.2 移动语义与性能优化(Move Semantics and Performance Optimization):利用移动语义减少拷贝开销,提升性能

    移动语义(Move Semantics)是 C++11 引入的一项重要特性,旨在减少不必要的对象拷贝,从而提升程序性能。SmallVector 作为现代 C++ 容器,自然也充分利用了移动语义。本节将深入探讨 SmallVector 如何借助移动语义进行性能优化,以及如何在实际开发中应用这些技巧。

    知识框架:

    拷贝语义(Copy Semantics)的开销
    ▮▮▮▮ⓑ 深拷贝与浅拷贝:对于包含动态分配内存的类(如 std::vector, std::string),拷贝操作通常需要进行深拷贝,即复制对象的所有数据,包括动态分配的内存。深拷贝开销较大。
    ▮▮▮▮ⓒ 不必要的拷贝:在很多情况下,对象的拷贝是不必要的,例如函数返回临时对象、对象赋值等。传统的拷贝语义会导致额外的性能开销。

    移动语义(Move Semantics)的核心思想
    ▮▮▮▮ⓑ 资源转移:移动语义的核心思想是将资源(如动态分配的内存)的所有权从一个对象转移到另一个对象,而不是进行深拷贝。
    ▮▮▮▮ⓒ 右值引用(Rvalue Reference):移动语义的实现依赖于右值引用 &&。右值引用可以绑定到临时对象或即将销毁的对象(右值),从而可以安全地“窃取”其资源。
    ▮▮▮▮ⓓ 移动构造函数(Move Constructor)与移动赋值运算符(Move Assignment Operator):类需要定义移动构造函数和移动赋值运算符来实现移动语义。移动操作通常只需要进行浅拷贝,并将源对象的资源置为空或有效可析构状态。

    SmallVector 与移动语义
    ▮▮▮▮ⓑ 高效元素操作:SmallVector 的 push_back(), emplace_back(), insert(), assign() 等修改容器内容的操作,都充分利用了移动语义。当插入或赋值的元素是右值时,会优先调用移动构造函数或移动赋值运算符,避免不必要的拷贝。
    ▮▮▮▮ⓒ 容器移动:SmallVector 自身也支持移动构造和移动赋值。移动 SmallVector 对象时,只会转移内部的指针和容量等信息,而不会复制元素数据,大幅提升移动效率。
    ▮▮▮▮ⓓ 返回值优化(Return Value Optimization, RVO)与移动语义的协同:编译器通常会进行返回值优化,直接在接收端构造返回值对象,结合移动语义,可以进一步减少拷贝开销。

    实战代码:

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 #include <folly/container/SmallVector.h>
    2 #include <iostream>
    3
    4 class MyString {
    5 public:
    6 MyString(const char* str = "") {
    7 std::cout << "Constructor called for: " << str << std::endl;
    8 size_ = std::strlen(str);
    9 data_ = new char[size_ + 1];
    10 std::strcpy(data_, str);
    11 }
    12 MyString(const MyString& other) {
    13 std::cout << "Copy constructor called for: " << other.data_ << std::endl;
    14 size_ = other.size_;
    15 data_ = new char[size_ + 1];
    16 std::strcpy(data_, other.data_);
    17 }
    18 MyString(MyString&& other) noexcept {
    19 std::cout << "Move constructor called for: " << other.data_ << std::endl;
    20 size_ = other.size_;
    21 data_ = other.data_;
    22 other.data_ = nullptr; // 源对象置为有效可析构状态
    23 other.size_ = 0;
    24 }
    25 ~MyString() {
    26 std::cout << "Destructor called for: ";
    27 if (data_) {
    28 std::cout << data_ << std::endl;
    29 delete[] data_;
    30 } else {
    31 std::cout << "(moved-from)" << std::endl;
    32 }
    33 }
    34
    35 private:
    36 char* data_;
    37 size_t size_;
    38 };
    39
    40 MyString getString() {
    41 return MyString("Hello, SmallVector!"); // 返回临时对象,触发移动语义
    42 }
    43
    44 int main() {
    45 folly::SmallVector<MyString, 2> sv;
    46
    47 std::cout << "Pushing back a copy:" << std::endl;
    48 MyString str1("String 1");
    49 sv.push_back(str1); // 插入左值,调用拷贝构造函数
    50
    51 std::cout << "\nPushing back a move:" << std::endl;
    52 sv.push_back(MyString("String 2")); // 插入右值,调用移动构造函数
    53
    54 std::cout << "\nPushing back a temporary object (getString()):" << std::endl;
    55 sv.push_back(getString()); // 插入临时对象,调用移动构造函数 (RVO 可能发生)
    56
    57 std::cout << "\nEmplacing back:" << std::endl;
    58 sv.emplace_back("String 3"); // 原位构造,避免拷贝和移动
    59
    60 return 0;
    61 }

    代码解析:

    MyString 类:自定义的字符串类,带有拷贝构造函数、移动构造函数和析构函数,用于观察拷贝和移动行为。
    getString() 函数:返回一个 MyString 临时对象(右值)。
    sv.push_back(str1);:插入左值 str1,调用 MyString 的拷贝构造函数。
    sv.push_back(MyString("String 2"));:插入右值 MyString("String 2"),调用 MyString 的移动构造函数。
    sv.push_back(getString());:插入 getString() 返回的临时对象,调用 MyString 的移动构造函数(编译器可能进行 RVO,直接在 SmallVector 内部构造对象,进一步优化)。
    sv.emplace_back("String 3");:使用 emplace_back() 原位构造 MyString 对象,避免了任何拷贝或移动操作。

    高级应用:

    处理大型对象:当 SmallVector 存储的对象体积较大,拷贝开销显著时,移动语义的优势更加明显。例如,存储大型矩阵、图像数据等。
    函数返回值优化:在函数返回 SmallVector 对象时,利用移动语义可以避免昂贵的容器拷贝,提高函数调用的效率。
    算法优化:在使用 STL 算法(如 std::sort, std::transform)操作 SmallVector 时,移动语义可以减少元素交换和移动的开销。

    API 全面解析:

    push_back(const T& value):插入元素,如果 value 是左值,则调用拷贝构造函数;如果 value 是右值,则调用移动构造函数。
    push_back(T&& value):强制移动插入元素,即使 value 是左值,也会尝试移动构造(需要 std::move 显式转换)。
    emplace_back(Args&&... args):原位构造元素,避免拷贝和移动。
    insert(iterator pos, const T& value) / insert(iterator pos, T&& value) / emplace(iterator pos, Args&&... args):插入元素到指定位置,同样支持拷贝、移动和原位构造。
    assign(std::initializer_list<T> il) / assign(InputIterator first, InputIterator last) / assign(size_type count, const T& value) / assign(size_type count, T&& value):赋值操作,支持移动语义。

    总结:

    移动语义是现代 C++ 性能优化的重要手段。SmallVector 通过充分利用移动语义,在元素插入、容器移动等操作中,有效地减少了不必要的拷贝开销,提升了程序性能。在实际开发中,我们应该养成使用移动语义的习惯,例如尽可能使用右值、使用 std::move 显式移动、使用 emplace_back 等原位构造方法,以充分发挥 SmallVector 的性能优势。


    3.3 emplace_back 与 in-place 构造(emplace_back and In-place Construction):高效元素插入方法,避免临时对象产生

    emplace_back() 是 C++11 引入的容器成员函数,与 push_back() 类似,用于在容器尾部添加元素。但 emplace_back() 的独特之处在于它支持 in-place 构造(原位构造),可以直接在容器内部构造元素,避免了临时对象的产生,从而在某些情况下能够提供更高的性能。本节将深入解析 emplace_back() 的工作原理、优势以及在 SmallVector 中的应用。

    知识框架:

    临时对象的产生与开销
    ▮▮▮▮ⓑ 临时对象的生命周期:临时对象通常在表达式结束后立即销毁。
    ▮▮▮▮ⓒ 拷贝与移动开销:使用 push_back() 插入元素时,通常需要先构造一个临时对象,然后将该临时对象拷贝或移动到容器内部。对于复杂对象,临时对象的构造、拷贝/移动和析构都会带来额外的开销。

    in-place 构造(原位构造)的概念
    ▮▮▮▮ⓑ 直接在容器内存中构造:in-place 构造是指直接在容器预留的内存空间中构造对象,避免了先创建临时对象再拷贝/移动到容器内部的过程。
    ▮▮▮▮ⓒ 减少对象生命周期管理开销:由于避免了临时对象的产生,也减少了临时对象的构造和析构开销。

    emplace_back() 的工作原理
    ▮▮▮▮ⓑ 可变参数模板(Variadic Templates):emplace_back() 使用可变参数模板,可以接受任意数量和类型的参数。
    ▮▮▮▮ⓒ 完美转发(Perfect Forwarding):emplace_back() 使用完美转发将接收到的参数转发给元素类型的构造函数,在容器内部直接构造元素。
    ▮▮▮▮ⓓ 避免临时对象:通过 in-place 构造和完美转发,emplace_back() 避免了先创建临时对象再拷贝/移动到容器内部的过程。

    emplace_back() 在 SmallVector 中的应用
    ▮▮▮▮ⓑ 高效插入复杂对象:当 SmallVector 存储的对象类型构造复杂,或者拷贝/移动开销较大时,使用 emplace_back() 可以显著提升插入性能。
    ▮▮▮▮ⓒ 减少内存分配次数:在某些情况下,in-place 构造可以减少内存分配的次数,例如在 SmallVector 栈上空间充足时,emplace_back() 可以直接在栈上构造对象,避免堆内存分配。

    实战代码:

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 #include <folly/container/SmallVector.h>
    2 #include <iostream>
    3
    4 class MyObject {
    5 public:
    6 MyObject(int a, double b, const std::string& c) : a_(a), b_(b), c_(c) {
    7 std::cout << "Constructor called for MyObject(" << a << ", " << b << ", " << c << ")" << std::endl;
    8 }
    9 MyObject(const MyObject& other) : a_(other.a_), b_(other.b_), c_(other.c_) {
    10 std::cout << "Copy constructor called for MyObject(" << a_ << ", " << b_ << ", " << c_ << ")" << std::endl;
    11 }
    12 MyObject(MyObject&& other) noexcept : a_(other.a_), b_(other.b_), c_(std::move(other.c_)) {
    13 std::cout << "Move constructor called for MyObject(" << a_ << ", " << b_ << ", " << c_ << ")" << std::endl;
    14 }
    15 ~MyObject() {
    16 std::cout << "Destructor called for MyObject(" << a_ << ", " << b_ << ", " << c_ << ")" << std::endl;
    17 }
    18
    19 private:
    20 int a_;
    21 double b_;
    22 std::string c_;
    23 };
    24
    25 int main() {
    26 folly::SmallVector<MyObject, 2> sv;
    27
    28 std::cout << "Using push_back:" << std::endl;
    29 sv.push_back(MyObject(1, 2.0, "push_back")); // 先构造临时对象,再移动到 SmallVector
    30
    31 std::cout << "\nUsing emplace_back:" << std::endl;
    32 sv.emplace_back(2, 3.0, "emplace_back"); // 直接在 SmallVector 内部构造对象
    33
    34 return 0;
    35 }

    代码解析:

    MyObject 类:自定义的类,构造函数、拷贝构造函数、移动构造函数和析构函数都带有输出信息,用于观察对象的构造和析构过程。
    sv.push_back(MyObject(1, 2.0, "push_back"));:使用 push_back() 插入元素,先在外部构造一个 MyObject 临时对象,然后将该临时对象移动到 SmallVector 内部。可以看到输出了 "Constructor", "Move constructor", "Destructor" 三个信息。
    sv.emplace_back(2, 3.0, "emplace_back");:使用 emplace_back() 插入元素,直接在 SmallVector 内部使用提供的参数 (2, 3.0, "emplace_back") 构造 MyObject 对象。可以看到只输出了 "Constructor" 和最终的 "Destructor" 信息,没有拷贝或移动构造函数的调用,表明避免了临时对象的产生。

    高级应用:

    性能敏感场景:在对性能要求极高的场景,如游戏开发、高性能计算等,emplace_back() 可以作为一种重要的性能优化手段。
    避免不必要的拷贝:当元素类型的拷贝构造函数开销较大时,或者拷贝操作不必要时,应优先使用 emplace_back()
    构造复杂对象:对于构造函数参数较多的复杂对象,emplace_back() 可以使代码更简洁,避免手动创建临时对象。

    API 全面解析:

    emplace_back(Args&&... args):在 SmallVector 尾部原位构造一个元素。Args&&... args 是可变参数,会被完美转发给元素类型的构造函数。返回值类型为 reference,指向新插入的元素。

    总结:

    emplace_back() 通过 in-place 构造和完美转发技术,提供了一种高效的元素插入方法,可以避免临时对象的产生,减少不必要的拷贝和移动开销,从而提升程序性能。在 SmallVector 中,emplace_back() 同样适用,尤其是在插入复杂对象或对性能有较高要求的场景下,应优先考虑使用 emplace_back() 来替代 push_back()


    3.4 自定义分配器(Custom Allocator)的应用(Application of Custom Allocator):高级内存管理技巧,定制化内存分配策略

    在 C++ 中,标准库容器(如 std::vector, std::map)允许用户自定义内存分配器(Allocator),以实现更精细的内存管理。SmallVector 同样支持自定义分配器。通过自定义分配器,我们可以根据特定应用场景的需求,定制内存分配策略,例如使用特定的内存池、共享内存、或者进行内存分配的性能监控和调试。本节将深入探讨自定义分配器的概念、实现方法以及在 SmallVector 中的应用。

    知识框架:

    默认分配器(Default Allocator)
    ▮▮▮▮ⓑ std::allocator:C++ 标准库提供的默认分配器是 std::allocator。它通常使用 ::operator new::operator delete 来进行内存的分配和释放。
    ▮▮▮▮ⓒ 适用场景:std::allocator 适用于大多数通用场景,但在某些特殊场景下可能不是最优选择。

    自定义分配器的需求
    ▮▮▮▮ⓑ 性能优化:在高性能应用中,默认分配器的性能可能成为瓶颈。自定义分配器可以使用更高效的内存池、预分配等策略来提升性能。
    ▮▮▮▮ⓒ 内存控制:在资源受限的环境下,需要更精细地控制内存分配,例如限制内存使用量、使用特定的内存区域等。
    ▮▮▮▮ⓓ 内存监控与调试:自定义分配器可以方便地添加内存分配的监控和调试功能,例如记录内存分配日志、检测内存泄漏等。
    ▮▮▮▮ⓔ 特殊内存区域:例如使用共享内存、持久化内存等特殊内存区域。

    自定义分配器的实现
    ▮▮▮▮ⓑ Allocator 概念:C++ 标准库对 Allocator 有一套严格的接口要求。自定义分配器需要满足这些接口要求,才能与标准库容器兼容。
    ▮▮▮▮ⓒ 必须提供的类型和成员函数:
    ▮▮▮▮▮▮▮▮❹ value_type:分配器分配的内存存储的对象类型。
    ▮▮▮▮▮▮▮▮❺ pointer, const_pointer, reference, const_reference:指针和引用的类型定义。
    ▮▮▮▮▮▮▮▮❻ allocate(size_t n):分配 nvalue_type 对象所需的内存。
    ▮▮▮▮▮▮▮▮❼ deallocate(pointer p, size_t n):释放之前分配的 nvalue_type 对象的内存。
    ▮▮▮▮▮▮▮▮❽ construct(pointer p, const T& value) / construct(pointer p, T&& value):在已分配的内存 p 上构造对象。
    ▮▮▮▮▮▮▮▮❾ destroy(pointer p):销毁 p 指向的对象。
    ▮▮▮▮ⓙ 可选提供的类型和成员函数:例如 max_size(), address(), operator==, operator!= 等。
    ▮▮▮▮ⓚ 状态与有状态分配器/无状态分配器:分配器可以是有状态的或无状态的。无状态分配器可以自由复制,有状态分配器则需要考虑状态的拷贝和共享。

    SmallVector 与自定义分配器
    ▮▮▮▮ⓑ 模板参数:SmallVector 的模板定义允许指定自定义分配器类型:folly::SmallVector<T, N, Allocator>,其中 Allocator 就是自定义分配器类型。
    ▮▮▮▮ⓒ 使用方法:在声明 SmallVector 对象时,可以传入自定义分配器实例。SmallVector 的内存分配和释放操作将委托给自定义分配器。

    实战代码:

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 #include <folly/container/SmallVector.h>
    2 #include <iostream>
    3 #include <memory>
    4
    5 // 自定义分配器示例:简单的计数分配器
    6 template <typename T>
    7 class CountingAllocator {
    8 public:
    9 using value_type = T;
    10 CountingAllocator() noexcept : allocated_count_(0), deallocated_count_(0) {}
    11 template <typename U> CountingAllocator(const CountingAllocator<U>&) noexcept : allocated_count_(0), deallocated_count_(0) {} // 允许从其他类型的 CountingAllocator 转换
    12
    13 T* allocate(std::size_t n) {
    14 std::cout << "Allocating " << n * sizeof(T) << " bytes" << std::endl;
    15 allocated_count_++;
    16 return static_cast<T*>(std::malloc(n * sizeof(T)));
    17 }
    18 void deallocate(T* p, std::size_t n) noexcept {
    19 std::cout << "Deallocating " << n * sizeof(T) << " bytes" << std::endl;
    20 deallocated_count_++;
    21 std::free(p);
    22 }
    23
    24 template <typename U, typename... Args>
    25 void construct(U* p, Args&&... args) {
    26 std::cout << "Constructing object at " << p << std::endl;
    27 ::new (static_cast<void*>(p)) U(std::forward<Args>(args)...);
    28 }
    29
    30 void destroy(T* p) noexcept {
    31 std::cout << "Destroying object at " << p << std::endl;
    32 p->~T();
    33 }
    34
    35 int getAllocatedCount() const { return allocated_count_; }
    36 int getDeallocatedCount() const { return deallocated_count_; }
    37
    38 private:
    39 int allocated_count_;
    40 int deallocated_count_;
    41 };
    42
    43 template <typename T, typename U>
    44 bool operator==(const CountingAllocator<T>&, const CountingAllocator<U>&) { return true; } // 无状态分配器,始终相等
    45 template <typename T, typename U>
    46 bool operator!=(const CountingAllocator<T>&, const CountingAllocator<U>&) { return false; }
    47
    48 int main() {
    49 CountingAllocator<int> allocator;
    50 folly::SmallVector<int, 2, CountingAllocator<int>> sv(allocator);
    51
    52 sv.push_back(1);
    53 sv.push_back(2);
    54 sv.push_back(3); // 触发堆内存分配
    55
    56 std::cout << "Allocated count: " << allocator.getAllocatedCount() << std::endl;
    57 std::cout << "Deallocated count: " << allocator.getDeallocatedCount() << std::endl;
    58
    59 return 0;
    60 }

    代码解析:

    CountingAllocator 类:自定义的计数分配器,用于统计内存分配和释放的次数,并在分配和释放内存时输出信息。
    allocate(size_t n):使用 std::malloc 分配内存,并增加分配计数。
    deallocate(T* p, size_t n):使用 std::free 释放内存,并增加释放计数。
    construct(U* p, Args&&... args):使用 placement new 在已分配的内存上构造对象。
    destroy(T* p):显式调用对象的析构函数。
    operator==operator!=:对于无状态分配器,通常返回 truefalse
    folly::SmallVector<int, 2, CountingAllocator<int>> sv(allocator);:声明 SmallVector 时,指定使用 CountingAllocator<int> 作为分配器类型,并传入分配器实例 allocator

    高级应用:

    内存池分配器:使用预先分配好的内存池来管理内存,减少动态内存分配的开销,提高性能。
    共享内存分配器:在多进程环境中,使用共享内存分配器可以让多个进程共享 SmallVector 容器的数据。
    调试分配器:用于内存泄漏检测、内存越界访问检测等调试目的。
    定制化内存策略:例如,根据对象的大小选择不同的内存分配策略,或者使用 NUMA 感知的内存分配器。

    API 全面解析:

    folly::SmallVector<T, N, Allocator = std::allocator<T>>:SmallVector 模板类的定义,第三个模板参数 Allocator 用于指定分配器类型,默认为 std::allocator<T>
    ⚝ 构造函数:SmallVector 的构造函数可以接受一个分配器实例作为参数,用于指定容器使用的分配器。
    get_allocator():返回容器当前使用的分配器实例。

    总结:

    自定义分配器是 C++ 中高级内存管理的重要技术。SmallVector 支持自定义分配器,为用户提供了更大的灵活性和控制权,可以根据具体的应用场景定制内存分配策略,实现性能优化、内存控制、监控调试等目标。掌握自定义分配器的使用方法,可以帮助我们更好地管理内存资源,构建更高效、更可靠的 C++ 程序。


    3.5 与 std::vector 的性能对比评测(Performance Comparison with std::vector):基准测试与性能分析,量化 SmallVector 的优势

    SmallVector 的设计目标之一就是在特定场景下提供优于 std::vector 的性能。本节将通过基准测试(Benchmark)的方法,对比 SmallVector 和 std::vector 在不同操作下的性能差异,量化 SmallVector 的性能优势,并分析其背后的原因。

    知识框架:

    性能评测的重要性
    ▮▮▮▮ⓑ 选择合适的容器:性能评测可以帮助我们了解不同容器的性能特点,从而根据实际应用场景选择最合适的容器。
    ▮▮▮▮ⓒ 优化代码性能:通过性能评测,可以发现代码中的性能瓶颈,并针对性地进行优化。
    ▮▮▮▮ⓓ 量化性能提升:性能评测可以量化优化措施带来的性能提升效果,为优化决策提供数据支持。

    基准测试(Benchmark)方法
    ▮▮▮▮ⓑ 选择合适的测试工具:常用的 C++ 基准测试工具有 Google Benchmark, Criterion, Hayai 等。
    ▮▮▮▮ⓒ 设计合理的测试用例:测试用例应尽可能覆盖容器的常用操作,例如插入、删除、访问、遍历等,并考虑不同数据规模和场景。
    ▮▮▮▮ⓓ 避免测试误差:需要注意测试环境的稳定性,避免其他因素干扰测试结果。多次运行取平均值可以减少随机误差。
    ▮▮▮▮ⓔ 性能指标:常用的性能指标包括执行时间、CPU 周期数、内存分配次数等。

    SmallVector vs. std::vector 的性能差异分析
    ▮▮▮▮ⓑ 栈上空间优势:当元素数量小于等于 SmallVector 的栈上容量 N 时,SmallVector 的所有操作都在栈上进行,避免了堆内存分配和释放的开销,速度更快。
    ▮▮▮▮ⓒ 堆上扩容开销:当元素数量超过 N 时,SmallVector 会在堆上分配内存,并进行动态扩容,此时性能与 std::vector 接近,甚至可能略有下降(因为可能存在数据从栈到堆的迁移开销)。
    ▮▮▮▮ⓓ 移动语义优势:SmallVector 和 std::vector 都支持移动语义,但在某些情况下,SmallVector 的栈上存储可能更有利于编译器进行优化,例如返回值优化(RVO)。
    ▮▮▮▮ⓔ 适用场景:SmallVector 在元素数量较小且可预测的场景下性能优势明显,例如存储固定大小的小数组、函数参数传递等。std::vector 更适用于元素数量不确定或可能很大的场景。

    性能评测关注点
    ▮▮▮▮ⓑ 不同操作的性能对比:例如,push_back(), insert(), operator[], 遍历等操作的性能对比。
    ▮▮▮▮ⓒ 不同数据规模的影响:测试不同元素数量下 SmallVector 和 std::vector 的性能差异。
    ▮▮▮▮ⓓ 不同元素类型的影响:测试存储不同类型(例如,POD 类型、复杂对象)时的性能差异。
    ▮▮▮▮ⓔ 编译优化选项的影响:不同的编译优化选项可能会影响性能测试结果。

    3.5.1 benchmark 代码示例(benchmark Code Example):使用 Google Benchmark 或其他工具进行性能测试

    以下示例代码使用 Google Benchmark 框架进行 SmallVector 和 std::vector 的性能对比测试。

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 #include <folly/container/SmallVector.h>
    2 #include <vector>
    3 #include <benchmark/benchmark.h>
    4
    5 // Benchmark for push_back operation with small size (within stack capacity)
    6 static void BM_SmallVector_PushBack_Small(benchmark::State& state) {
    7 for (auto _ : state) {
    8 folly::SmallVector<int, 8> sv;
    9 for (int i = 0; i < state.range(0); ++i) {
    10 sv.push_back(i);
    11 }
    12 }
    13 }
    14 BENCHMARK(BM_SmallVector_PushBack_Small)->RangeMultiplier(2)->Range(1, 8);
    15
    16 static void BM_StdVector_PushBack_Small(benchmark::State& state) {
    17 for (auto _ : state) {
    18 std::vector<int> v;
    19 for (int i = 0; i < state.range(0); ++i) {
    20 v.push_back(i);
    21 }
    22 }
    23 }
    24 BENCHMARK(BM_StdVector_PushBack_Small)->RangeMultiplier(2)->Range(1, 8);
    25
    26 // Benchmark for push_back operation with large size (exceeding stack capacity)
    27 static void BM_SmallVector_PushBack_Large(benchmark::State& state) {
    28 for (auto _ : state) {
    29 folly::SmallVector<int, 8> sv;
    30 for (int i = 0; i < state.range(0); ++i) {
    31 sv.push_back(i);
    32 }
    33 }
    34 }
    35 BENCHMARK(BM_SmallVector_PushBack_Large)->RangeMultiplier(10)->Range(10, 10000);
    36
    37 static void BM_StdVector_PushBack_Large(benchmark::State& state) {
    38 for (auto _ : state) {
    39 std::vector<int> v;
    40 for (int i = 0; i < state.range(0); ++i) {
    41 v.push_back(i);
    42 }
    43 }
    44 }
    45 BENCHMARK(BM_StdVector_PushBack_Large)->RangeMultiplier(10)->Range(10, 10000);
    46
    47 // Benchmark for element access (operator[])
    48 static void BM_SmallVector_Access(benchmark::State& state) {
    49 folly::SmallVector<int, 10000> sv;
    50 for (int i = 0; i < 10000; ++i) {
    51 sv.push_back(i);
    52 }
    53 for (auto _ : state) {
    54 for (int i = 0; i < 10000; ++i) {
    55 benchmark::DoNotOptimize(sv[i]); // Prevent compiler optimization
    56 }
    57 }
    58 }
    59 BENCHMARK(BM_SmallVector_Access);
    60
    61 static void BM_StdVector_Access(benchmark::State& state) {
    62 std::vector<int> v;
    63 for (int i = 0; i < 10000; ++i) {
    64 v.push_back(i);
    65 }
    66 for (auto _ : state) {
    67 for (int i = 0; i < 10000; ++i) {
    68 benchmark::DoNotOptimize(v[i]); // Prevent compiler optimization
    69 }
    70 }
    71 }
    72 BENCHMARK(BM_StdVector_Access);
    73
    74
    75 int main(int argc, char** argv) {
    76 benchmark::Initialize(&argc, argv);
    77 if (benchmark::ReportUnrecognizedArguments(argc, argv)) return 1;
    78 benchmark::RunSpecifiedBenchmarks();
    79 return 0;
    80 }

    代码解析:

    ⚝ 引入头文件:folly/container/SmallVector.h, vector, benchmark/benchmark.h
    BM_SmallVector_PushBack_SmallBM_StdVector_PushBack_Small:测试小尺寸(1-8 个元素) push_back() 操作的性能。SmallVector 的栈上容量设置为 8。
    BM_SmallVector_PushBack_LargeBM_StdVector_PushBack_Large:测试大尺寸(10-10000 个元素) push_back() 操作的性能,超出 SmallVector 的栈上容量。
    BM_SmallVector_AccessBM_StdVector_Access:测试元素访问 operator[] 的性能。
    BENCHMARK(...)->RangeMultiplier(..)->Range(..):Google Benchmark 的宏,用于定义基准测试函数和参数范围。
    benchmark::DoNotOptimize(...):防止编译器过度优化,确保测试代码被实际执行。
    main() 函数:初始化 Google Benchmark 并运行指定的基准测试。

    运行与结果分析:

    编译并运行上述 benchmark 代码,可以得到 SmallVector 和 std::vector 在不同操作下的性能数据。通常情况下,可以观察到:

    小尺寸 push_back() 操作:SmallVector 性能明显优于 std::vector,因为 SmallVector 使用栈上空间,避免了堆内存分配。
    大尺寸 push_back() 操作:SmallVector 和 std::vector 性能接近,但 SmallVector 可能略慢,因为存在栈到堆的数据迁移开销。
    元素访问 operator[]:SmallVector 和 std::vector 性能非常接近,因为都是直接内存访问。

    总结:

    通过基准测试,我们可以量化 SmallVector 在特定场景下的性能优势。SmallVector 在元素数量较小且不超过栈上容量时,由于栈上内存分配的优势,性能明显优于 std::vector。在元素数量较大时,两者性能接近。因此,在选择容器时,需要根据实际应用场景的元素数量特点,权衡 SmallVector 和 std::vector 的优缺点,选择最合适的容器。对于元素数量通常较小且可预测的场景,SmallVector 是一个优秀的性能优化选择。


    END_OF_CHAPTER

    4. chapter 4: SmallVector 高级应用与实战案例(SmallVector Advanced Applications and Practical Cases)

    4.1 在高性能计算中的应用(Applications in High-Performance Computing)

    高性能计算(High-Performance Computing, HPC)领域对性能有着极致的追求,每一个细微的优化都可能对整体计算效率产生显著影响。SmallVector 因其独特的设计理念,在 HPC 场景中能够发挥重要作用,尤其是在处理数据规模相对较小且对性能敏感的计算任务时。

    数据局部性与缓存优化:在 HPC 应用中,数据局部性是影响性能的关键因素之一。SmallVector 的栈上内存分配特性,使得数据更有可能被分配在连续的内存空间中,从而提高了缓存命中率。对于频繁访问的小型数据集,这可以显著减少内存访问延迟,提升计算速度。

    减少动态内存分配开销:HPC 应用通常包含大量的循环和迭代计算,频繁的动态内存分配和释放会带来显著的性能开销。SmallVector 的固定容量优化,允许在栈上预分配一定大小的内存,避免了在数据规模较小时的堆内存分配,从而减少了动态内存管理的开销。

    适用于小型数据集和临时数据结构:许多 HPC 算法在执行过程中会产生大量的小型数据集或临时数据结构,例如:
    ⚝ 线性代数运算中的小型向量和矩阵。
    ⚝ 数值模拟中的局部网格数据。
    ⚝ 粒子物理模拟中的粒子信息集合。
    ⚝ 信号处理中的小段数据帧。

    对于这些场景,SmallVector 可以作为 std::vector 的高效替代品。当数据量不超过 SmallVector 的栈上容量时,其性能优势尤为明显。

    实战代码:使用 SmallVector 加速向量点积运算

    假设我们需要频繁计算两个向量的点积,向量长度通常较小。我们可以使用 SmallVector 来存储向量数据,并比较其与 std::vector 在性能上的差异。

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 #include <vector>
    2 #include <numeric>
    3 #include <chrono>
    4 #include <iostream>
    5 #include <folly/container/SmallVector.h>
    6
    7 using namespace std;
    8 using namespace folly;
    9
    10 double dot_product_std_vector(const vector<double>& a, const vector<double>& b) {
    11 return inner_product(a.begin(), a.end(), b.begin(), 0.0);
    12 }
    13
    14 double dot_product_small_vector(const SmallVector<double>& a, const SmallVector<double>& b) {
    15 return inner_product(a.begin(), a.end(), b.begin(), 0.0);
    16 }
    17
    18 int main() {
    19 int size = 10; // 向量长度较小
    20 int iterations = 1000000; // 迭代次数
    21
    22 vector<double> std_vec_a(size, 1.0);
    23 vector<double> std_vec_b(size, 2.0);
    24 SmallVector<double> small_vec_a(size, 1.0);
    25 SmallVector<double> small_vec_b(size, 2.0);
    26
    27 // std::vector 性能测试
    28 auto start_std = chrono::high_resolution_clock::now();
    29 for (int i = 0; i < iterations; ++i) {
    30 dot_product_std_vector(std_vec_a, std_vec_b);
    31 }
    32 auto end_std = chrono::high_resolution_clock::now();
    33 chrono::duration<double> duration_std = end_std - start_std;
    34 cout << "std::vector dot product time: " << duration_std.count() << " seconds" << endl;
    35
    36 // SmallVector 性能测试
    37 auto start_small = chrono::high_resolution_clock::now();
    38 for (int i = 0; i < iterations; ++i) {
    39 dot_product_small_vector(small_vec_a, small_vec_b);
    40 }
    41 auto end_small = chrono::high_resolution_clock::now();
    42 chrono::duration<double> duration_small = end_small - start_small;
    43 cout << "SmallVector dot product time: " << duration_small.count() << " seconds" << endl;
    44
    45 return 0;
    46 }

    在这个示例中,我们分别使用 std::vectorSmallVector 进行了向量点积运算的性能测试。在向量长度较小且迭代次数较多的情况下,SmallVector 通常会表现出更优的性能,这主要是由于其栈上分配减少了动态内存分配的开销。

    总结:在 HPC 领域,SmallVector 适用于对性能要求苛刻,且数据规模相对较小的场景。通过合理利用 SmallVector,可以有效地提升 HPC 应用的计算效率。

    4.2 在网络编程中的应用(Applications in Network Programming)

    网络编程中,数据包的处理是核心任务之一。网络数据包通常具有大小受限且生命周期短暂的特点,这使得 SmallVector 在网络编程领域拥有独特的优势。

    高效的网络数据包缓冲区:网络数据包的大小通常不会太大,例如以太网帧的最大传输单元(Maximum Transmission Unit, MTU)通常在 1500 字节左右。SmallVector 可以作为高效的网络数据包缓冲区,利用其栈上空间存储数据包内容。

    减少网络数据包处理延迟:在高性能网络服务中,降低数据包处理延迟至关重要。SmallVector 避免了堆内存分配,减少了内存分配和垃圾回收的开销,从而降低了网络数据包的处理延迟。

    适用于协议解析和数据包构建:网络协议解析和数据包构建过程中,经常需要处理和操作小型的字节序列。SmallVector 提供了方便的 API 和高效的内存管理,非常适合用于实现协议解析器和数据包构建器。

    实战代码:使用 SmallVector 实现简单的网络数据包处理

    假设我们需要处理简单的网络数据包,数据包结构包含包头和包体,包头固定大小,包体大小可变但通常较小。我们可以使用 SmallVector 来存储整个数据包。

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 #include <iostream>
    2 #include <vector>
    3 #include <folly/container/SmallVector.h>
    4
    5 using namespace std;
    6 using namespace folly;
    7
    8 // 模拟网络数据包结构
    9 struct NetworkPacket {
    10 SmallVector<uint8_t> data;
    11
    12 NetworkPacket(size_t header_size, const uint8_t* payload, size_t payload_size) {
    13 data.resize(header_size + payload_size);
    14 // 填充包头 (示例,实际应用中需要根据协议填充)
    15 for (size_t i = 0; i < header_size; ++i) {
    16 data[i] = static_cast<uint8_t>(i);
    17 }
    18 // 拷贝 payload
    19 memcpy(data.data() + header_size, payload, payload_size);
    20 }
    21
    22 void process_packet() {
    23 cout << "Processing packet, size: " << data.size() << endl;
    24 // 实际的网络包处理逻辑...
    25 }
    26 };
    27
    28 int main() {
    29 uint8_t payload[] = {0x01, 0x02, 0x03, 0x04, 0x05};
    30 NetworkPacket packet(10, payload, sizeof(payload));
    31 packet.process_packet();
    32
    33 return 0;
    34 }

    在这个示例中,NetworkPacket 结构体使用 SmallVector<uint8_t> 来存储网络数据包。由于网络数据包的大小通常有限,SmallVector 可以有效地管理数据包缓冲区,并减少内存分配开销。

    总结:在网络编程领域,SmallVector 尤其适用于处理网络数据包这种小而频繁的数据。它可以作为高效的网络数据包缓冲区,降低处理延迟,提升网络服务的性能。

    4.3 在游戏开发中的应用(Applications in Game Development)

    游戏开发对性能的要求极高,尤其是在需要实时渲染和高速响应的游戏引擎中。SmallVector 在游戏开发中可以用于优化各种数据结构,提升游戏性能和帧率。

    游戏对象组件管理:现代游戏引擎通常采用组件式架构(Component-Based Architecture),游戏对象由多个组件构成。对于组件数量较少的游戏对象,可以使用 SmallVector 来存储组件列表。

    粒子系统:粒子系统是游戏中常用的特效技术,每个粒子通常包含少量属性(位置、速度、颜色等)。使用 SmallVector 可以高效地存储和管理粒子的属性数据。

    小型游戏对象和临时数据:游戏中存在大量小型游戏对象和临时数据,例如:
    ⚝ 碰撞检测中的碰撞点列表。
    ⚝ 渲染过程中的顶点索引列表。
    ⚝ 游戏逻辑中的临时状态数据。

    对于这些小型数据集合,SmallVector 可以提供比 std::vector 更高效的内存管理和访问速度。

    实战代码:使用 SmallVector 优化游戏对象组件管理

    假设我们有一个简单的游戏引擎,游戏对象由组件构成,每个游戏对象最多包含少量组件。我们可以使用 SmallVector 来存储游戏对象的组件列表。

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 #include <iostream>
    2 #include <vector>
    3 #include <folly/container/SmallVector.h>
    4
    5 using namespace std;
    6 using namespace folly;
    7
    8 // 游戏组件基类
    9 class GameComponent {
    10 public:
    11 virtual ~GameComponent() = default;
    12 virtual void update() = 0;
    13 };
    14
    15 // 示例组件:位置组件
    16 class PositionComponent : public GameComponent {
    17 public:
    18 PositionComponent(float x, float y) : x_(x), y_(y) {}
    19 void update() override {
    20 cout << "Position: (" << x_ << ", " << y_ << ")" << endl;
    21 }
    22 private:
    23 float x_;
    24 float y_;
    25 };
    26
    27 // 示例组件:渲染组件
    28 class RenderComponent : public GameComponent {
    29 public:
    30 RenderComponent(const string& model_name) : model_name_(model_name) {}
    31 void update() override {
    32 cout << "Rendering model: " << model_name_ << endl;
    33 }
    34 private:
    35 string model_name_;
    36 };
    37
    38 // 游戏对象类
    39 class GameObject {
    40 public:
    41 using ComponentList = SmallVector<unique_ptr<GameComponent>>; // 使用 SmallVector 存储组件
    42 ComponentList components;
    43
    44 void addComponent(unique_ptr<GameComponent> component) {
    45 components.push_back(move(component));
    46 }
    47
    48 void updateComponents() {
    49 for (const auto& component : components) {
    50 component->update();
    51 }
    52 }
    53 };
    54
    55 int main() {
    56 GameObject obj;
    57 obj.addComponent(make_unique<PositionComponent>(10.0f, 20.0f));
    58 obj.addComponent(make_unique<RenderComponent>("player_model"));
    59
    60 obj.updateComponents();
    61
    62 return 0;
    63 }

    在这个示例中,GameObject 类使用 SmallVector<unique_ptr<GameComponent>> 来存储组件列表。由于游戏对象通常组件数量有限,SmallVector 可以有效地管理组件,并减少内存分配开销,从而提升游戏性能。

    总结:在游戏开发中,SmallVector 可以用于优化游戏引擎的各种数据结构,尤其是在处理组件管理、粒子系统和小型游戏对象等场景时,能够提升游戏性能和帧率,为玩家带来更流畅的游戏体验。

    4.4 与其他 Folly 组件的协同工作(Collaboration with Other Folly Components)

    Folly 库作为一个全面的 C++ 库,提供了许多高效且实用的组件。SmallVector 可以与其他 Folly 组件协同工作,构建更强大、更高效的应用程序。

    FBString 的协同FBStringFolly 提供的字符串类,它在小字符串优化方面与 SmallVector 有相似的设计理念。SmallVector<FBString> 可以用于存储字符串集合,结合两者的优化特性,可以高效地处理字符串数据。例如,在文本处理、日志分析等场景中,可以使用 SmallVector<FBString> 来存储和操作文本行或日志条目。

    FBVector 的协同FBVectorFolly 提供的另一个动态数组容器,它在某些方面比 std::vector 更加高效。SmallVectorFBVector 可以根据不同的场景和需求进行选择和组合使用。例如,可以使用 SmallVector 存储小型数据集,当数据规模超过 SmallVector 的容量时,可以将数据转移到 FBVector 中进行处理。

    Folly Algorithms 的协同Folly 提供了丰富的算法库,包括排序、查找、变换等。SmallVector 可以与 Folly Algorithms 中的算法无缝集成,利用这些高效的算法对 SmallVector 中的数据进行处理。例如,可以使用 folly::sortSmallVector 中的元素进行排序,使用 folly::binary_searchSmallVector 中查找元素。

    Futures/Promises 的协同:在异步编程中,Futures/Promises 是常用的并发编程模型。SmallVector 可以用于在异步任务之间传递数据。例如,一个异步任务可以使用 SmallVector 存储计算结果,并将 SmallVector 作为 Future 的返回值传递给后续的任务。

    实战代码:使用 SmallVector<FBString> 存储和处理文本行

    假设我们需要读取一个文本文件,并将每行文本存储到容器中进行处理。我们可以使用 SmallVector<FBString> 来存储文本行。

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 #include <iostream>
    2 #include <fstream>
    3 #include <string>
    4 #include <folly/container/SmallVector.h>
    5 #include <folly/FBString.h>
    6
    7 using namespace std;
    8 using namespace folly;
    9
    10 int main() {
    11 ifstream file("input.txt");
    12 if (!file.is_open()) {
    13 cerr << "Error opening file" << endl;
    14 return 1;
    15 }
    16
    17 SmallVector<FBString> lines;
    18 string line;
    19 while (getline(file, line)) {
    20 lines.emplace_back(line); // 使用 emplace_back 直接构造 FBString
    21 }
    22 file.close();
    23
    24 cout << "Read " << lines.size() << " lines from file." << endl;
    25
    26 // 遍历并处理文本行
    27 for (const auto& fb_line : lines) {
    28 cout << "Line: " << fb_line.toStdString() << endl; // 转换为 std::string 输出
    29 // 实际的文本行处理逻辑...
    30 }
    31
    32 return 0;
    33 }

    在这个示例中,我们使用 SmallVector<FBString> 存储从文件中读取的文本行。FBString 的小字符串优化和 SmallVector 的栈上分配特性结合,可以高效地处理文本数据。

    总结SmallVector 可以与 Folly 库中的其他组件,如 FBString, FBVector, Folly Algorithms, Futures/Promises 等协同工作,构建更强大、更高效的应用程序。通过合理利用 Folly 库的组件,可以充分发挥 SmallVector 的优势,提升程序性能和开发效率。

    4.5 案例分析:开源项目中的 SmallVector 应用(Case Study: SmallVector Applications in Open Source Projects)

    学习开源项目如何使用 SmallVector 是深入理解其应用场景和最佳实践的有效途径。许多知名的开源项目,尤其是在高性能计算、网络编程和游戏开发领域,都采用了 Folly 库,并从中受益。

    查找开源项目中的 SmallVector 用例:可以通过 GitHub、GitLab 等代码托管平台搜索使用 FollySmallVector 的开源项目。关键词可以包括 "folly", "SmallVector", "facebook/folly" 等。

    分析项目代码:选择一些具有代表性的开源项目,分析其代码库中 SmallVector 的使用方式。重点关注以下几个方面:
    SmallVector 的应用场景:项目在哪些模块或功能中使用了 SmallVector
    SmallVector 的容量选择:项目如何选择 SmallVector 的容量参数?
    SmallVector 的性能收益:项目是否在文档或代码注释中提及使用 SmallVector 带来的性能提升?
    SmallVector 与其他 Folly 组件的协同:项目是否将 SmallVector 与其他 Folly 组件结合使用?

    案例示例:folly itself

    Folly 库自身就大量使用了 SmallVector。在 Folly 的源代码中搜索 folly::SmallVector,可以找到大量的用例。例如,在 FollyFBString 实现中,SmallVector 被用于存储小字符串的字符数据,实现了小字符串优化。

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 // 摘自 folly/FBString.h (简化版本)
    2 template <typename CharType, size_t N>
    3 class SmallString {
    4 private:
    5 SmallVector<CharType, N> data_; // 使用 SmallVector 存储小字符串数据
    6 size_t size_;
    7 public:
    8 // ...
    9 };

    FBString 的实现中,SmallVector 作为内部数据存储结构,充分利用了 SmallVector 的栈上分配和固定容量优化特性,提升了小字符串的处理效率。

    案例示例:ClickHouse

    ClickHouse 是一个开源的列式数据库管理系统,以其高性能而闻名。ClickHouse 也使用了 Folly 库,并在其代码库中使用了 SmallVector。例如,在 ClickHouse 的某些数据结构和算法实现中,SmallVector 被用于存储小型数据集合,以提升性能。

    案例分析总结:通过分析开源项目中的 SmallVector 用例,我们可以学习到:
    SmallVector 在高性能场景中的广泛应用。
    ⚝ 如何根据实际场景选择合适的 SmallVector 容量。
    SmallVector 与其他库或组件协同工作的模式。
    ⚝ 开源项目对性能优化的极致追求和实践经验。

    学习开源项目的实践经验,可以帮助我们更好地理解和应用 SmallVector,并在自己的项目中充分发挥其优势。

    END_OF_CHAPTER

    5. chapter 5: SmallVector API 全面解析(SmallVector API Comprehensive Analysis)

    本章将深入探讨 Folly 库中 SmallVector 容器提供的各种 API,旨在为读者提供一个全面、权威的 API 参考指南。我们将详细解析每个 API 的功能、参数、返回值、异常处理以及使用场景,并通过实战代码示例,帮助读者彻底掌握 SmallVector 的使用方法,充分发挥其在性能和效率方面的优势。无论你是初学者还是经验丰富的工程师,本章内容都将成为你深入理解和应用 SmallVector 的宝贵资源。

    5.1 构造函数与赋值运算符详解(Constructors and Assignment Operators Detailed Analysis)

    SmallVector 提供了多种构造函数和赋值运算符,以支持灵活的对象创建和赋值操作。本节将逐一深入解析这些构造函数和赋值运算符,帮助读者理解它们的功能、适用场景以及使用注意事项。

    5.1.1 默认构造函数(Default Constructor)

    函数签名(Function Signature):

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 SmallVector() noexcept;

    描述(Description):
    默认构造函数创建一个空的 SmallVector 对象,其大小(size)为 0。由于 SmallVector 的特性,它会在栈上预留一定的空间(由模板参数 N 决定),但此时并不分配任何元素。

    参数(Parameters):
    无(None)

    返回值(Return Value):
    无(None)

    异常(Exceptions):
    noexcept - 此函数保证不抛出任何异常。

    代码示例(Code Example):

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 #include <folly/container/SmallVector.h>
    2 #include <iostream>
    3
    4 int main() {
    5 folly::SmallVector<int, 4> sv; // 创建一个可以容纳 4 个 int 的 SmallVector
    6 std::cout << "Initial size: " << sv.size() << std::endl; // 输出初始大小
    7 std::cout << "Initial capacity: " << sv.capacity() << std::endl; // 输出初始容量
    8 return 0;
    9 }

    输出:

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 Initial size: 0
    2 Initial capacity: 4

    要点(Key Points):
    ① 默认构造函数是最简单的构造方式,适用于在后续代码中动态添加元素的情况。
    ② 初始容量由模板参数 N 决定,但不占用实际存储空间,只有在添加元素时才会使用。

    5.1.2 容量构造函数(Capacity Constructor)

    函数签名(Function Signature):

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 explicit SmallVector(size_type capacity);

    描述(Description):
    容量构造函数创建一个空的 SmallVector 对象,并预先分配至少能容纳 capacity 个元素的空间。这可以避免在后续添加元素时频繁的内存重新分配,提高性能。

    参数(Parameters):
    capacity (size_type): 期望的初始容量大小。

    返回值(Return Value):
    无(None)

    异常(Exceptions):
    std::bad_alloc: 如果内存分配失败,可能会抛出此异常。

    代码示例(Code Example):

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 #include <folly/container/SmallVector.h>
    2 #include <iostream>
    3
    4 int main() {
    5 folly::SmallVector<int, 4> sv(10); // 创建一个初始容量为 10 的 SmallVector
    6 std::cout << "Initial size: " << sv.size() << std::endl; // 输出初始大小
    7 std::cout << "Initial capacity: " << sv.capacity() << std::endl; // 输出初始容量
    8 return 0;
    9 }

    输出 (输出可能因实现而异,但容量至少为 10):

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 Initial size: 0
    2 Initial capacity: 10

    要点(Key Points):
    explicit 关键字防止隐式类型转换,必须显式调用构造函数。
    ② 预先指定容量适用于已知大致元素数量的场景,可以提升性能。
    ③ 实际分配的容量可能大于等于 capacity,具体取决于实现。

    5.1.3 范围构造函数(Range Constructor)

    函数签名(Function Signature):

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 template <class InputIterator>
    2 SmallVector(InputIterator first, InputIterator last);

    描述(Description):
    范围构造函数使用迭代器 [first, last) 指定的范围内的元素来初始化 SmallVector。它会复制范围内的所有元素到新的 SmallVector 对象中。

    参数(Parameters):
    first (InputIterator): 输入范围的起始迭代器。
    last (InputIterator): 输入范围的结束迭代器。

    返回值(Return Value):
    无(None)

    异常(Exceptions):
    ⚝ 可能抛出输入迭代器操作或元素复制过程中抛出的任何异常。
    std::bad_alloc: 如果内存分配失败,可能会抛出此异常。

    代码示例(Code Example):

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 #include <folly/container/SmallVector.h>
    2 #include <vector>
    3 #include <iostream>
    4
    5 int main() {
    6 std::vector<int> vec = {1, 2, 3, 4, 5};
    7 folly::SmallVector<int, 4> sv(vec.begin(), vec.end()); // 使用 vector 初始化 SmallVector
    8
    9 std::cout << "Size: " << sv.size() << std::endl; // 输出大小
    10 std::cout << "Capacity: " << sv.capacity() << std::endl; // 输出容量
    11 for (int val : sv) {
    12 std::cout << val << " "; // 输出 SmallVector 中的元素
    13 }
    14 std::cout << std::endl;
    15 return 0;
    16 }

    输出 (输出可能因实现而异,但容量至少为 5):

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 Size: 5
    2 Capacity: 5
    3 1 2 3 4 5

    要点(Key Points):
    ① 适用于从其他容器或数据范围初始化 SmallVector 的场景。
    ② 会复制范围内的元素,如果元素类型是自定义类,会调用其拷贝构造函数。
    ③ 迭代器类型只需要满足输入迭代器的要求。

    5.1.4 初始化列表构造函数(Initializer List Constructor)

    函数签名(Function Signature):

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 SmallVector(std::initializer_list<value_type> il);

    描述(Description):
    初始化列表构造函数使用 std::initializer_list 提供的元素列表来初始化 SmallVector。这是一种简洁方便的初始化方式,尤其适用于在代码中直接指定元素值。

    参数(Parameters):
    il (std::initializer_list<value_type>): 初始化列表。

    返回值(Return Value):
    无(None)

    异常(Exceptions):
    ⚝ 可能抛出元素复制过程中抛出的任何异常。
    std::bad_alloc: 如果内存分配失败,可能会抛出此异常。

    代码示例(Code Example):

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 #include <folly/container/SmallVector.h>
    2 #include <iostream>
    3
    4 int main() {
    5 folly::SmallVector<int, 4> sv = {1, 2, 3, 4, 5}; // 使用初始化列表初始化 SmallVector
    6
    7 std::cout << "Size: " << sv.size() << std::endl; // 输出大小
    8 std::cout << "Capacity: " << sv.capacity() << std::endl; // 输出容量
    9 for (int val : sv) {
    10 std::cout << val << " "; // 输出 SmallVector 中的元素
    11 }
    12 std::cout << std::endl;
    13 return 0;
    14 }

    输出 (输出可能因实现而异,但容量至少为 5):

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 Size: 5
    2 Capacity: 5
    3 1 2 3 4 5

    要点(Key Points):
    ① C++11 引入的特性,语法简洁,易于使用。
    ② 适用于在编译时已知元素值的场景。
    ③ 同样会复制列表中的元素。

    5.1.5 拷贝构造函数(Copy Constructor)

    函数签名(Function Signature):

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 SmallVector(const SmallVector& other);

    描述(Description):
    拷贝构造函数创建一个新的 SmallVector 对象,它是 other 对象的深拷贝。这意味着新对象拥有 other 对象所有元素的副本,并且拥有独立的内存空间。

    参数(Parameters):
    other (const SmallVector&): 要拷贝的 SmallVector 对象。

    返回值(Return Value):
    无(None)

    异常(Exceptions):
    ⚝ 可能抛出元素拷贝过程中抛出的任何异常。
    std::bad_alloc: 如果内存分配失败,可能会抛出此异常。

    代码示例(Code Example):

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 #include <folly/container/SmallVector.h>
    2 #include <iostream>
    3
    4 int main() {
    5 folly::SmallVector<int, 4> sv1 = {1, 2, 3};
    6 folly::SmallVector<int, 4> sv2 = sv1; // 使用拷贝构造函数创建 sv2
    7
    8 sv1[0] = 100; // 修改 sv1 的元素,sv2 不受影响
    9
    10 std::cout << "sv1: ";
    11 for (int val : sv1) {
    12 std::cout << val << " ";
    13 }
    14 std::cout << std::endl;
    15
    16 std::cout << "sv2: ";
    17 for (int val : sv2) {
    18 std::cout << val << " ";
    19 }
    20 std::cout << std::endl;
    21 return 0;
    22 }

    输出:

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 sv1: 100 2 3
    2 sv2: 1 2 3

    要点(Key Points):
    ① 执行深拷贝,修改拷贝源对象不会影响拷贝目标对象。
    ② 如果元素类型是自定义类,会调用其拷贝构造函数。
    ③ 拷贝构造函数在对象按值传递或作为函数返回值时会被隐式调用。

    5.1.6 移动构造函数(Move Constructor)

    函数签名(Function Signature):

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 SmallVector(SmallVector&& other) noexcept;

    描述(Description):
    移动构造函数创建一个新的 SmallVector 对象,并从 other 对象“移动”资源,而不是拷贝。这意味着新对象会接管 other 对象的内存空间和元素,而 other 对象将处于有效的、但未指定的状态。移动构造函数通常比拷贝构造函数更高效,尤其是在处理大型对象时。

    参数(Parameters):
    other (SmallVector&&): 要移动的 SmallVector 对象(右值引用)。

    返回值(Return Value):
    无(None)

    异常(Exceptions):
    noexcept - 此函数保证不抛出任何异常。

    代码示例(Code Example):

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 #include <folly/container/SmallVector.h>
    2 #include <iostream>
    3
    4 folly::SmallVector<int, 4> createSmallVector() {
    5 folly::SmallVector<int, 4> sv = {1, 2, 3};
    6 return sv; // 返回右值,触发移动构造
    7 }
    8
    9 int main() {
    10 folly::SmallVector<int, 4> sv2 = createSmallVector(); // 使用移动构造函数创建 sv2
    11
    12 std::cout << "sv2: ";
    13 for (int val : sv2) {
    14 std::cout << val << " ";
    15 }
    16 std::cout << std::endl;
    17
    18 // sv1 的状态变为有效但未指定,不应再访问其元素 (此处为了演示,实际使用中应避免)
    19 // std::cout << "sv1: " << sv1.size() << std::endl; // 可能会崩溃或输出不确定值
    20
    21 return 0;
    22 }

    输出:

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 sv2: 1 2 3

    要点(Key Points):
    ① 执行浅拷贝,资源所有权转移,避免了深拷贝的开销。
    ② 移动构造函数通常在右值引用或 std::move 的情况下被调用。
    ③ 移动源对象 other 在移动后处于有效但未指定的状态,不应再依赖其内部数据。

    5.1.7 拷贝赋值运算符(Copy Assignment Operator)

    函数签名(Function Signature):

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 SmallVector& operator=(const SmallVector& other);

    描述(Description):
    拷贝赋值运算符将 other 对象的内容拷贝赋值给当前对象。如果当前对象已经分配了内存,并且容量不足以容纳 other 对象的所有元素,则可能需要重新分配内存。

    参数(Parameters):
    other (const SmallVector&): 要拷贝赋值的 SmallVector 对象。

    返回值(Return Value):
    SmallVector&: 对当前对象的引用 (*this)。

    异常(Exceptions):
    ⚝ 可能抛出元素拷贝过程中抛出的任何异常。
    std::bad_alloc: 如果内存分配失败,可能会抛出此异常。

    代码示例(Code Example):

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 #include <folly/container/SmallVector.h>
    2 #include <iostream>
    3
    4 int main() {
    5 folly::SmallVector<int, 4> sv1 = {1, 2};
    6 folly::SmallVector<int, 4> sv2 = {3, 4, 5};
    7
    8 sv1 = sv2; // 使用拷贝赋值运算符将 sv2 赋值给 sv1
    9
    10 sv2[0] = 300; // 修改 sv2 的元素,sv1 不受影响
    11
    12 std::cout << "sv1: ";
    13 for (int val : sv1) {
    14 std::cout << val << " ";
    15 }
    16 std::cout << std::endl;
    17
    18 std::cout << "sv2: ";
    19 for (int val : sv2) {
    20 std::cout << val << " ";
    21 }
    22 std::cout << std::endl;
    23 return 0;
    24 }

    输出:

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 sv1: 3 4 5
    2 sv2: 300 4 5

    要点(Key Points):
    ① 执行深拷贝赋值,修改赋值源对象不会影响赋值目标对象。
    ② 赋值运算符需要处理自赋值的情况 (sv1 = sv1),但 SmallVector 通常能正确处理。
    ③ 返回对自身对象的引用,支持链式赋值操作 (sv1 = sv2 = sv3)。

    5.1.8 移动赋值运算符(Move Assignment Operator)

    函数签名(Function Signature):

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 SmallVector& operator=(SmallVector&& other) noexcept;

    描述(Description):
    移动赋值运算符将 other 对象的内容移动赋值给当前对象。与移动构造函数类似,它会转移资源所有权,避免不必要的拷贝操作。

    参数(Parameters):
    other (SmallVector&&): 要移动赋值的 SmallVector 对象(右值引用)。

    返回值(Return Value):
    SmallVector&: 对当前对象的引用 (*this)。

    异常(Exceptions):
    noexcept - 此函数保证不抛出任何异常。

    代码示例(Code Example):

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 #include <folly/container/SmallVector.h>
    2 #include <iostream>
    3
    4 int main() {
    5 folly::SmallVector<int, 4> sv1 = {1, 2};
    6 folly::SmallVector<int, 4> sv2 = {3, 4, 5};
    7
    8 sv1 = std::move(sv2); // 使用移动赋值运算符将 sv2 移动赋值给 sv1
    9
    10 std::cout << "sv1: ";
    11 for (int val : sv1) {
    12 std::cout << val << " ";
    13 }
    14 std::cout << std::endl;
    15
    16 // sv2 的状态变为有效但未指定,不应再访问其元素 (此处为了演示,实际使用中应避免)
    17 // std::cout << "sv2: " << sv2.size() << std::endl; // 可能会崩溃或输出不确定值
    18 return 0;
    19 }

    输出:

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 sv1: 3 4 5

    要点(Key Points):
    ① 执行浅拷贝赋值,资源所有权转移,提升性能。
    ② 移动赋值运算符通常在右值引用或 std::move 的情况下被调用。
    ③ 移动源对象 other 在移动后处于有效但未指定的状态,不应再依赖其内部数据。
    ④ 返回对自身对象的引用,支持链式赋值操作。

    5.2 容量函数详解(Capacity Functions Detailed Analysis)

    SmallVector 提供了一系列容量函数,用于查询和管理容器的内存容量和大小。合理使用这些函数可以帮助我们更好地理解和控制 SmallVector 的内存使用,优化程序性能。

    5.2.1 size()

    函数签名(Function Signature):

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 size_type size() const noexcept;

    描述(Description):
    size() 函数返回 SmallVector 中当前元素的数量,即容器的大小。

    参数(Parameters):
    无(None)

    返回值(Return Value):
    size_type: 容器中元素的数量。

    异常(Exceptions):
    noexcept - 此函数保证不抛出任何异常。

    代码示例(Code Example):

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 #include <folly/container/SmallVector.h>
    2 #include <iostream>
    3
    4 int main() {
    5 folly::SmallVector<int, 4> sv = {1, 2, 3};
    6 std::cout << "Size: " << sv.size() << std::endl; // 输出元素数量
    7 return 0;
    8 }

    输出:

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 Size: 3

    要点(Key Points):
    size() 返回的是逻辑大小,即用户可见的元素数量。
    ② 时间复杂度为 \(O(1)\)。

    5.2.2 capacity()

    函数签名(Function Signature):

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 size_type capacity() const noexcept;

    描述(Description):
    capacity() 函数返回 SmallVector 当前已分配的存储空间可以容纳的元素数量,即容器的容量。

    参数(Parameters):
    无(None)

    返回值(Return Value):
    size_type: 容器的容量。

    异常(Exceptions):
    noexcept - 此函数保证不抛出任何异常。

    代码示例(Code Example):

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 #include <folly/container/SmallVector.h>
    2 #include <iostream>
    3
    4 int main() {
    5 folly::SmallVector<int, 4> sv = {1, 2, 3};
    6 std::cout << "Capacity: " << sv.capacity() << std::endl; // 输出容器容量
    7 return 0;
    8 }

    输出 (输出可能因实现而异,但容量至少为 3):

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 Capacity: 4

    要点(Key Points):
    capacity() 返回的是物理容量,表示容器在不重新分配内存的情况下可以容纳的最大元素数量。
    capacity() 总是大于等于 size()
    ③ 时间复杂度为 \(O(1)\)。

    5.2.3 max_size()

    函数签名(Function Signature):

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 size_type max_size() const noexcept;

    描述(Description):
    max_size() 函数返回 SmallVector 理论上可以容纳的最大元素数量,这通常受限于系统内存和实现限制。

    参数(Parameters):
    无(None)

    返回值(Return Value):
    size_type: 容器的最大容量。

    异常(Exceptions):
    noexcept - 此函数保证不抛出任何异常。

    代码示例(Code Example):

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 #include <folly/container/SmallVector.h>
    2 #include <iostream>
    3
    4 int main() {
    5 folly::SmallVector<int, 4> sv;
    6 std::cout << "Max size: " << sv.max_size() << std::endl; // 输出最大容量
    7 return 0;
    8 }

    输出 (输出值会非常大):

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 Max size: 4611686018427387903

    要点(Key Points):
    max_size() 返回的是理论上的最大值,实际应用中不太可能达到。
    ② 主要用于了解容器容量的上限。
    ③ 时间复杂度为 \(O(1)\)。

    5.2.4 empty()

    函数签名(Function Signature):

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 bool empty() const noexcept;

    描述(Description):
    empty() 函数检查 SmallVector 是否为空,即容器中是否没有任何元素。

    参数(Parameters):
    无(None)

    返回值(Return Value):
    bool: 如果容器为空,返回 true;否则返回 false

    异常(Exceptions):
    noexcept - 此函数保证不抛出任何异常。

    代码示例(Code Example):

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 #include <folly/container/SmallVector.h>
    2 #include <iostream>
    3
    4 int main() {
    5 folly::SmallVector<int, 4> sv1;
    6 folly::SmallVector<int, 4> sv2 = {1, 2, 3};
    7
    8 std::cout << "sv1 is empty: " << sv1.empty() << std::endl; // 检查 sv1 是否为空
    9 std::cout << "sv2 is empty: " << sv2.empty() << std::endl; // 检查 sv2 是否为空
    10 return 0;
    11 }

    输出:

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 sv1 is empty: true
    2 sv2 is empty: false

    要点(Key Points):
    ① 判断容器是否为空的便捷方法,等价于 size() == 0
    ② 时间复杂度为 \(O(1)\)。

    5.2.5 reserve()

    函数签名(Function Signature):

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 void reserve(size_type n);

    描述(Description):
    reserve() 函数请求 SmallVector 预留至少能容纳 n 个元素的存储空间。如果在调用 reserve() 之前,容器的 capacity() 小于 n,则会重新分配内存,使其容量至少达到 n。如果 n 小于或等于当前的 capacity(),则 reserve() 不会产生任何效果。

    参数(Parameters):
    n (size_type): 期望的最小容量。

    返回值(Return Value):
    无(None)

    异常(Exceptions):
    std::length_error: 如果 n 大于 max_size(),则抛出此异常。
    std::bad_alloc: 如果内存分配失败,可能会抛出此异常。

    代码示例(Code Example):

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 #include <folly/container/SmallVector.h>
    2 #include <iostream>
    3
    4 int main() {
    5 folly::SmallVector<int, 4> sv;
    6 std::cout << "Initial capacity: " << sv.capacity() << std::endl; // 输出初始容量
    7
    8 sv.reserve(10); // 预留容量为 10
    9 std::cout << "Capacity after reserve(10): " << sv.capacity() << std::endl; // 输出预留后的容量
    10
    11 sv.reserve(5); // 预留容量为 5 (小于当前容量,无效果)
    12 std::cout << "Capacity after reserve(5): " << sv.capacity() << std::endl; // 输出预留后的容量
    13 return 0;
    14 }

    输出 (输出可能因实现而异,但容量变化符合预期):

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 Initial capacity: 4
    2 Capacity after reserve(10): 10
    3 Capacity after reserve(5): 10

    要点(Key Points):
    ① 用于预先分配内存,避免在后续添加元素时频繁的内存重新分配,提高性能。
    reserve() 只会增加容量,不会改变 size()
    ③ 如果预留的容量小于当前容量,不会缩小容量。

    5.2.6 shrink_to_fit()

    函数签名(Function Signature):

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 void shrink_to_fit();

    描述(Description):
    shrink_to_fit() 函数尝试释放 SmallVector 中未使用的内存空间,将容器的 capacity() 减小到与 size() 相匹配的大小,或者一个更小的值,以节省内存。但请注意,具体的实现可能不会真正缩小容量,这取决于分配器的策略。

    参数(Parameters):
    无(None)

    返回值(Return Value):
    无(None)

    异常(Exceptions):
    std::bad_alloc: 如果内存分配失败(在某些实现中,缩小容量可能涉及重新分配内存),可能会抛出此异常。

    代码示例(Code Example):

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 #include <folly/container/SmallVector.h>
    2 #include <iostream>
    3
    4 int main() {
    5 folly::SmallVector<int, 4> sv;
    6 sv.reserve(100); // 预留大容量
    7 sv.push_back(1);
    8 sv.push_back(2);
    9 std::cout << "Capacity before shrink_to_fit: " << sv.capacity() << std::endl; // 输出 shrink_to_fit 前的容量
    10
    11 sv.shrink_to_fit(); // 尝试缩小容量
    12 std::cout << "Capacity after shrink_to_fit: " << sv.capacity() << std::endl; // 输出 shrink_to_fit 后的容量
    13 return 0;
    14 }

    输出 (输出可能因实现而异,但容量应该减小):

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 Capacity before shrink_to_fit: 100
    2 Capacity after shrink_to_fit: 2

    要点(Key Points):
    ① 用于释放不再需要的内存空间,减少内存占用。
    shrink_to_fit() 只是一个请求,具体的缩小行为取决于实现和分配器。
    ③ 调用 shrink_to_fit() 后,容器的迭代器、指针和引用仍然有效。

    5.3 元素访问函数详解(Element Access Functions Detailed Analysis)

    SmallVector 提供多种方式来访问容器中的元素,包括索引访问、范围访问以及首尾元素访问等。这些函数使得我们可以方便快捷地读取和修改容器中的元素。

    5.3.1 operator[]

    函数签名(Function Signature):

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 reference operator[](size_type pos);
    2 const_reference operator[](size_type pos) const;

    描述(Description):
    operator[] 提供对 SmallVector 中指定位置 pos 元素的直接访问。它不进行边界检查,因此如果 pos 超出有效范围 [0, size() - 1],则行为未定义(Undefined Behavior),可能导致程序崩溃或数据损坏。

    参数(Parameters):
    pos (size_type): 要访问元素的索引位置,范围为 [0, size() - 1]

    返回值(Return Value):
    reference: 对指定位置元素的引用(非 const 版本)。
    const_reference: 对指定位置元素的常量引用(const 版本)。

    异常(Exceptions):
    noexcept - 此函数保证不抛出任何异常。

    代码示例(Code Example):

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 #include <folly/container/SmallVector.h>
    2 #include <iostream>
    3
    4 int main() {
    5 folly::SmallVector<int, 4> sv = {10, 20, 30};
    6
    7 std::cout << "Element at index 1: " << sv[1] << std::endl; // 访问索引为 1 的元素
    8
    9 sv[0] = 100; // 修改索引为 0 的元素
    10 std::cout << "Element at index 0 after modification: " << sv[0] << std::endl;
    11
    12 // 注意:以下代码是错误的,因为索引 3 超出范围,会导致未定义行为
    13 // std::cout << sv[3] << std::endl; // 错误用法,可能崩溃
    14 return 0;
    15 }

    输出:

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 Element at index 1: 20
    2 Element at index 0 after modification: 100

    要点(Key Points):
    ① 提供快速的元素访问方式,时间复杂度为 \(O(1)\)。
    不进行边界检查,使用时务必确保索引有效,避免未定义行为。
    const 版本用于只读访问,非 const 版本用于读写访问。

    5.3.2 at()

    函数签名(Function Signature):

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 reference at(size_type pos);
    2 const_reference at(size_type pos) const;

    描述(Description):
    at() 函数也提供对 SmallVector 中指定位置 pos 元素的访问,但与 operator[] 不同的是,at() 会进行边界检查。如果 pos 超出有效范围 [0, size() - 1],则会抛出 std::out_of_range 异常。

    参数(Parameters):
    pos (size_type): 要访问元素的索引位置,范围为 [0, size() - 1]

    返回值(Return Value):
    reference: 对指定位置元素的引用(非 const 版本)。
    const_reference: 对指定位置元素的常量引用(const 版本)。

    异常(Exceptions):
    std::out_of_range: 如果 pos 超出有效范围,则抛出此异常。

    代码示例(Code Example):

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 #include <folly/container/SmallVector.h>
    2 #include <iostream>
    3 #include <stdexcept>
    4
    5 int main() {
    6 folly::SmallVector<int, 4> sv = {10, 20, 30};
    7
    8 std::cout << "Element at index 1: " << sv.at(1) << std::endl; // 访问索引为 1 的元素
    9
    10 try {
    11 std::cout << sv.at(3) << std::endl; // 访问超出范围的索引,会抛出异常
    12 } catch (const std::out_of_range& e) {
    13 std::cerr << "Out of range exception caught: " << e.what() << std::endl;
    14 }
    15 return 0;
    16 }

    输出:

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 Element at index 1: 20
    2 Out of range exception caught: folly::SmallVector::at: index out of range

    要点(Key Points):
    ① 提供安全的元素访问方式,时间复杂度为 \(O(1)\)。
    进行边界检查,超出范围会抛出异常,有助于提高程序的健壮性。
    const 版本用于只读访问,非 const 版本用于读写访问。
    ④ 在需要确保索引有效性的场景下,推荐使用 at()

    5.3.3 front()

    函数签名(Function Signature):

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 reference front();
    2 const_reference front() const;

    描述(Description):
    front() 函数返回 SmallVector 中第一个元素的引用。调用 front() 之前,必须确保容器非空,否则行为未定义。

    参数(Parameters):
    无(None)

    返回值(Return Value):
    reference: 对第一个元素的引用(非 const 版本)。
    const_reference: 对第一个元素的常量引用(const 版本)。

    异常(Exceptions):
    noexcept - 此函数保证不抛出任何异常。
    但如果容器为空,则行为未定义。

    代码示例(Code Example):

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 #include <folly/container/SmallVector.h>
    2 #include <iostream>
    3
    4 int main() {
    5 folly::SmallVector<int, 4> sv = {10, 20, 30};
    6
    7 if (!sv.empty()) {
    8 std::cout << "First element: " << sv.front() << std::endl; // 访问第一个元素
    9 sv.front() = 100; // 修改第一个元素
    10 std::cout << "First element after modification: " << sv.front() << std::endl;
    11 } else {
    12 std::cout << "SmallVector is empty." << std::endl;
    13 }
    14 return 0;
    15 }

    输出:

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 First element: 10
    2 First element after modification: 100

    要点(Key Points):
    ① 快速访问第一个元素,时间复杂度为 \(O(1)\)。
    调用前必须确保容器非空,否则可能导致未定义行为。
    const 版本用于只读访问,非 const 版本用于读写访问。

    5.3.4 back()

    函数签名(Function Signature):

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 reference back();
    2 const_reference back() const;

    描述(Description):
    back() 函数返回 SmallVector 中最后一个元素的引用。调用 back() 之前,必须确保容器非空,否则行为未定义。

    参数(Parameters):
    无(None)

    返回值(Return Value):
    reference: 对最后一个元素的引用(非 const 版本)。
    const_reference: 对最后一个元素的常量引用(const 版本)。

    异常(Exceptions):
    noexcept - 此函数保证不抛出任何异常。
    但如果容器为空,则行为未定义。

    代码示例(Code Example):

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 #include <folly/container/SmallVector.h>
    2 #include <iostream>
    3
    4 int main() {
    5 folly::SmallVector<int, 4> sv = {10, 20, 30};
    6
    7 if (!sv.empty()) {
    8 std::cout << "Last element: " << sv.back() << std::endl; // 访问最后一个元素
    9 sv.back() = 300; // 修改最后一个元素
    10 std::cout << "Last element after modification: " << sv.back() << std::endl;
    11 } else {
    12 std::cout << "SmallVector is empty." << std::endl;
    13 }
    14 return 0;
    15 }

    输出:

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 Last element: 30
    2 Last element after modification: 300

    要点(Key Points):
    ① 快速访问最后一个元素,时间复杂度为 \(O(1)\)。
    调用前必须确保容器非空,否则可能导致未定义行为。
    const 版本用于只读访问,非 const 版本用于读写访问。

    5.3.5 data()

    函数签名(Function Signature):

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 pointer data() noexcept;
    2 const_pointer data() const noexcept;

    描述(Description):
    data() 函数返回指向 SmallVector 内部存储数组首元素的指针。如果 SmallVector 为空,则返回值未指定,但可以解引用(C++11 标准之后)。通常返回 nullptr 当容器为空。

    参数(Parameters):
    无(None)

    返回值(Return Value):
    pointer: 指向首元素的指针(非 const 版本)。
    const_pointer: 指向首元素的常量指针(const 版本)。

    异常(Exceptions):
    noexcept - 此函数保证不抛出任何异常。

    代码示例(Code Example):

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 #include <folly/container/SmallVector.h>
    2 #include <iostream>
    3
    4 int main() {
    5 folly::SmallVector<int, 4> sv = {10, 20, 30};
    6
    7 int* ptr = sv.data(); // 获取指向首元素的指针
    8 std::cout << "First element using data(): " << *ptr << std::endl; // 通过指针访问首元素
    9
    10 for (size_t i = 0; i < sv.size(); ++i) {
    11 std::cout << *(ptr + i) << " "; // 使用指针遍历元素
    12 }
    13 std::cout << std::endl;
    14 return 0;
    15 }

    输出:

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 First element using data(): 10
    2 10 20 30

    要点(Key Points):
    ① 提供对底层数组的直接访问,可以用于与 C 风格 API 交互或进行底层操作。
    ② 返回的指针类型与元素类型匹配。
    如果容器为空,返回的指针可能为 nullptr 或其他未指定值,具体取决于实现。 建议在使用前检查容器是否为空。
    ④ 通过 data() 获取的指针,可以像操作普通数组一样操作 SmallVector 的元素。

    5.4 修改器函数详解(Modifier Functions Detailed Analysis)

    SmallVector 提供了一系列修改器函数,用于添加、删除、插入、清除和调整容器中的元素。这些函数使得我们可以灵活地操作 SmallVector 的内容。

    5.4.1 push_back()

    函数签名(Function Signature):

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 void push_back(const value_type& value);
    2 void push_back(value_type&& value);

    描述(Description):
    push_back() 函数在 SmallVector 的末尾添加一个新元素。它有两个重载版本:
    ⚝ 接受左值引用 (const value_type&) 的版本会拷贝元素。
    ⚝ 接受右值引用 (value_type&&) 的版本会移动元素(如果元素类型支持移动语义)。

    参数(Parameters):
    value (const value_type&value_type&&): 要添加到末尾的元素。

    返回值(Return Value):
    无(None)

    异常(Exceptions):
    ⚝ 可能抛出元素拷贝或移动过程中抛出的任何异常。
    std::bad_alloc: 如果需要重新分配内存且分配失败,可能会抛出此异常。

    代码示例(Code Example):

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 #include <folly/container/SmallVector.h>
    2 #include <iostream>
    3
    4 int main() {
    5 folly::SmallVector<int, 4> sv;
    6
    7 sv.push_back(10); // 添加元素 10
    8 sv.push_back(20); // 添加元素 20
    9 sv.push_back(30); // 添加元素 30
    10
    11 std::cout << "SmallVector after push_back: ";
    12 for (int val : sv) {
    13 std::cout << val << " ";
    14 }
    15 std::cout << std::endl;
    16 std::cout << "Size: " << sv.size() << std::endl;
    17 std::cout << "Capacity: " << sv.capacity() << std::endl;
    18 return 0;
    19 }

    输出 (输出可能因实现而异,但容量变化符合预期):

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 SmallVector after push_back: 10 20 30
    2 Size: 3
    3 Capacity: 4

    要点(Key Points):
    ① 在末尾添加元素的常用方法,时间复杂度平均为 \(O(1)\),最坏情况(需要重新分配内存)为 \(O(n)\)。
    ② 优先使用移动版本,避免不必要的拷贝操作,尤其是在添加临时对象或右值时。
    ③ 如果添加元素后超出当前容量,SmallVector 会自动重新分配内存,通常以指数方式增长容量。

    5.4.2 emplace_back()

    函数签名(Function Signature):

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 template <class... Args>
    2 reference emplace_back(Args&&... args);

    描述(Description):
    emplace_back() 函数在 SmallVector 的末尾就地构造一个新元素。它接受构造元素的参数,并在容器内部直接使用这些参数构造元素,避免了临时对象的创建和拷贝或移动操作。

    参数(Parameters):
    args... (Args&&...): 用于构造新元素的参数,参数类型和数量取决于元素类型的构造函数。

    返回值(Return Value):
    reference: 对新添加元素的引用。

    异常(Exceptions):
    ⚝ 可能抛出元素构造过程中抛出的任何异常。
    std::bad_alloc: 如果需要重新分配内存且分配失败,可能会抛出此异常。

    代码示例(Code Example):

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 #include <folly/container/SmallVector.h>
    2 #include <iostream>
    3
    4 struct MyStruct {
    5 int value;
    6 MyStruct(int v) : value(v) {
    7 std::cout << "MyStruct constructed with value: " << value << std::endl;
    8 }
    9 MyStruct(const MyStruct& other) : value(other.value) {
    10 std::cout << "MyStruct copy constructed with value: " << value << std::endl;
    11 }
    12 MyStruct(MyStruct&& other) noexcept : value(other.value) {
    13 std::cout << "MyStruct move constructed with value: " << value << std::endl;
    14 }
    15 };
    16
    17 int main() {
    18 folly::SmallVector<MyStruct, 4> sv;
    19
    20 std::cout << "Using emplace_back:" << std::endl;
    21 sv.emplace_back(10); // 就地构造 MyStruct 对象,避免拷贝或移动
    22
    23 std::cout << "Using push_back:" << std::endl;
    24 MyStruct temp(20);
    25 sv.push_back(temp); // 先构造临时对象,再拷贝到 SmallVector 中
    26
    27 std::cout << "SmallVector size: " << sv.size() << std::endl;
    28 return 0;
    29 }

    输出 (输出可能因编译器优化而略有不同,但 emplace_back 应该避免拷贝构造):

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 Using emplace_back:
    2 MyStruct constructed with value: 10
    3 Using push_back:
    4 MyStruct constructed with value: 20
    5 MyStruct copy constructed with value: 20
    6 SmallVector size: 2

    要点(Key Points):
    ① 高效的元素添加方法,尤其适用于元素类型构造复杂或开销较大的情况。
    ② 避免了临时对象的创建和拷贝或移动,直接在容器内部构造元素。
    ③ 时间复杂度与 push_back() 相同,平均为 \(O(1)\),最坏情况为 \(O(n)\)。
    emplace_back() 是 C++11 引入的特性。

    5.4.3 pop_back()

    函数签名(Function Signature):

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 void pop_back();

    描述(Description):
    pop_back() 函数移除 SmallVector 的最后一个元素。调用 pop_back() 之前,必须确保容器非空,否则行为未定义。

    参数(Parameters):
    无(None)

    返回值(Return Value):
    无(None)

    异常(Exceptions):
    noexcept - 此函数保证不抛出任何异常。
    但如果容器为空,则行为未定义。

    代码示例(Code Example):

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 #include <folly/container/SmallVector.h>
    2 #include <iostream>
    3
    4 int main() {
    5 folly::SmallVector<int, 4> sv = {10, 20, 30};
    6
    7 if (!sv.empty()) {
    8 sv.pop_back(); // 移除最后一个元素
    9 std::cout << "SmallVector after pop_back: ";
    10 for (int val : sv) {
    11 std::cout << val << " ";
    12 }
    13 std::cout << std::endl;
    14 std::cout << "Size: " << sv.size() << std::endl;
    15 } else {
    16 std::cout << "SmallVector is empty." << std::endl;
    17 }
    18 return 0;
    19 }

    输出:

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 SmallVector after pop_back: 10 20
    2 Size: 2

    要点(Key Points):
    ① 移除末尾元素的常用方法,时间复杂度为 \(O(1)\)。
    调用前必须确保容器非空,否则可能导致未定义行为。
    pop_back() 只会减少 size(),不会改变 capacity()

    5.4.4 insert()

    函数签名(Function Signature):

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 iterator insert(const_iterator pos, const value_type& value);
    2 iterator insert(const_iterator pos, value_type&& value);
    3 iterator insert(const_iterator pos, size_type count, const value_type& value);
    4 template <class InputIterator>
    5 iterator insert(const_iterator pos, InputIterator first, InputIterator last);
    6 iterator insert(const_iterator pos, std::initializer_list<value_type> il);

    描述(Description):
    insert() 函数在 SmallVector 的指定位置 pos 插入一个或多个新元素。它提供了多个重载版本,支持插入单个元素、多个相同元素、范围内的元素以及初始化列表中的元素。

    参数(Parameters):
    pos (const_iterator): 插入位置的迭代器。
    value (const value_type&value_type&&): 要插入的元素(单个元素插入版本)。
    count (size_type): 要插入的元素数量(多个相同元素插入版本)。
    first (InputIterator): 输入范围的起始迭代器(范围插入版本)。
    last (InputIterator): 输入范围的结束迭代器(范围插入版本)。
    il (std::initializer_list<value_type>): 初始化列表(初始化列表插入版本)。

    返回值(Return Value):
    iterator: 指向新插入的第一个元素的迭代器,或者如果 count 为 0,则返回 pos

    异常(Exceptions):
    ⚝ 可能抛出元素拷贝或移动过程中抛出的任何异常。
    std::bad_alloc: 如果需要重新分配内存且分配失败,可能会抛出此异常。

    代码示例(Code Example):

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 #include <folly/container/SmallVector.h>
    2 #include <vector>
    3 #include <iostream>
    4
    5 int main() {
    6 folly::SmallVector<int, 4> sv = {10, 20, 30};
    7
    8 // 插入单个元素
    9 auto it1 = sv.insert(sv.begin() + 1, 15); // 在索引 1 处插入 15
    10 std::cout << "SmallVector after single insert: ";
    11 for (int val : sv) {
    12 std::cout << val << " ";
    13 }
    14 std::cout << std::endl;
    15 std::cout << "Iterator to inserted element: " << *it1 << std::endl;
    16
    17 // 插入多个相同元素
    18 sv.insert(sv.end(), 2, 50); // 在末尾插入 2 个 50
    19 std::cout << "SmallVector after multiple insert: ";
    20 for (int val : sv) {
    21 std::cout << val << " ";
    22 }
    23 std::cout << std::endl;
    24
    25 // 插入范围元素
    26 std::vector<int> vec = {60, 70};
    27 sv.insert(sv.begin(), vec.begin(), vec.end()); // 在开头插入 vector 的元素
    28 std::cout << "SmallVector after range insert: ";
    29 for (int val : sv) {
    30 std::cout << val << " ";
    31 }
    32 std::cout << std::endl;
    33
    34 // 插入初始化列表元素
    35 sv.insert(sv.begin() + 3, {80, 90}); // 在索引 3 处插入初始化列表的元素
    36 std::cout << "SmallVector after initializer list insert: ";
    37 for (int val : sv) {
    38 std::cout << val << " ";
    39 }
    40 std::cout << std::endl;
    41
    42 return 0;
    43 }

    输出 (输出可能因实现而异,但元素插入位置和顺序符合预期):

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 SmallVector after single insert: 10 15 20 30
    2 Iterator to inserted element: 15
    3 SmallVector after multiple insert: 10 15 20 30 50 50
    4 SmallVector after range insert: 60 70 10 15 20 30 50 50
    5 SmallVector after initializer list insert: 60 70 10 80 90 15 20 30 50 50

    要点(Key Points):
    ① 在指定位置插入元素,时间复杂度平均为 \(O(n)\)(因为需要移动插入位置之后的元素)。
    ② 插入位置由迭代器指定,需要使用有效的迭代器。
    ③ 多个重载版本提供了灵活的插入方式。
    ④ 插入元素后,迭代器可能会失效,需要重新获取。

    5.4.5 emplace()

    函数签名(Function Signature):

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 template <class... Args>
    2 iterator emplace(const_iterator pos, Args&&... args);

    描述(Description):
    emplace() 函数在 SmallVector 的指定位置 pos 就地构造一个新元素。与 emplace_back() 类似,它接受构造元素的参数,并在容器内部直接使用这些参数构造元素,避免了临时对象的创建和拷贝或移动操作。

    参数(Parameters):
    pos (const_iterator): 插入位置的迭代器。
    args... (Args&&...): 用于构造新元素的参数,参数类型和数量取决于元素类型的构造函数。

    返回值(Return Value):
    iterator: 指向新插入元素的迭代器。

    异常(Exceptions):
    ⚝ 可能抛出元素构造过程中抛出的任何异常。
    std::bad_alloc: 如果需要重新分配内存且分配失败,可能会抛出此异常。

    代码示例(Code Example):

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 #include <folly/container/SmallVector.h>
    2 #include <iostream>
    3
    4 struct MyStruct {
    5 int value;
    6 MyStruct(int v) : value(v) {
    7 std::cout << "MyStruct constructed with value: " << value << std::endl;
    8 }
    9 MyStruct(const MyStruct& other) : value(other.value) {
    10 std::cout << "MyStruct copy constructed with value: " << value << std::endl;
    11 }
    12 MyStruct(MyStruct&& other) noexcept : value(other.value) {
    13 std::cout << "MyStruct move constructed with value: " << value << std::endl;
    14 }
    15 };
    16
    17 int main() {
    18 folly::SmallVector<MyStruct, 4> sv = {MyStruct(10), MyStruct(20)};
    19
    20 std::cout << "Using emplace:" << std::endl;
    21 sv.emplace(sv.begin() + 1, 15); // 在索引 1 处就地构造 MyStruct 对象
    22
    23 std::cout << "SmallVector size: " << sv.size() << std::endl;
    24 return 0;
    25 }

    输出 (输出可能因编译器优化而略有不同,但 emplace 应该避免拷贝构造):

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 MyStruct constructed with value: 10
    2 MyStruct constructed with value: 20
    3 Using emplace:
    4 MyStruct constructed with value: 15
    5 SmallVector size: 3

    要点(Key Points):
    ① 高效的元素插入方法,尤其适用于元素类型构造复杂或开销较大的情况。
    ② 避免了临时对象的创建和拷贝或移动,直接在容器内部构造元素。
    ③ 时间复杂度与 insert() 相同,平均为 \(O(n)\)。
    emplace() 是 C++11 引入的特性。

    5.4.6 erase()

    函数签名(Function Signature):

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 iterator erase(const_iterator pos);
    2 iterator erase(const_iterator first, const_iterator last);

    描述(Description):
    erase() 函数从 SmallVector 中移除指定位置或范围内的元素。它有两个重载版本:
    ⚝ 移除单个元素(通过迭代器 pos 指定位置)。
    ⚝ 移除范围内的元素(通过迭代器 [first, last) 指定范围)。

    参数(Parameters):
    pos (const_iterator): 要移除元素的迭代器(单个元素移除版本)。
    first (const_iterator): 要移除范围的起始迭代器(范围移除版本)。
    last (const_iterator): 要移除范围的结束迭代器(范围移除版本)。

    返回值(Return Value):
    iterator: 指向被移除元素之后元素的迭代器,或者如果移除的是最后一个元素,则返回 end()

    异常(Exceptions):
    ⚝ 可能抛出元素析构过程中抛出的任何异常。

    代码示例(Code Example):

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 #include <folly/container/SmallVector.h>
    2 #include <iostream>
    3
    4 int main() {
    5 folly::SmallVector<int, 4> sv = {10, 20, 30, 40, 50};
    6
    7 // 移除单个元素
    8 auto it1 = sv.erase(sv.begin() + 2); // 移除索引 2 的元素 (30)
    9 std::cout << "SmallVector after single erase: ";
    10 for (int val : sv) {
    11 std::cout << val << " ";
    12 }
    13 std::cout << std::endl;
    14 std::cout << "Iterator after erase: " << *it1 << std::endl; // 指向 40
    15
    16 // 移除范围元素
    17 sv.erase(sv.begin(), sv.begin() + 2); // 移除前两个元素 (10, 20)
    18 std::cout << "SmallVector after range erase: ";
    19 for (int val : sv) {
    20 std::cout << val << " ";
    21 }
    22 std::cout << std::endl;
    23
    24 return 0;
    25 }

    输出 (输出可能因实现而异,但元素移除位置和顺序符合预期):

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 SmallVector after single erase: 10 20 40 50
    2 Iterator after erase: 40
    3 SmallVector after range erase: 40 50

    要点(Key Points):
    ① 移除指定位置或范围内的元素,时间复杂度平均为 \(O(n)\)(因为需要移动被移除元素之后的元素)。
    ② 移除位置或范围由迭代器指定,需要使用有效的迭代器。
    ③ 移除元素后,迭代器可能会失效,需要重新获取。
    ④ 返回的迭代器指向被移除元素之后的位置,方便链式操作。

    5.4.7 clear()

    函数签名(Function Signature):

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 void clear() noexcept;

    描述(Description):
    clear() 函数移除 SmallVector 中的所有元素,使其变为空容器。容器的 size() 变为 0,但 capacity() 通常保持不变。

    参数(Parameters):
    无(None)

    返回值(Return Value):
    无(None)

    异常(Exceptions):
    noexcept - 此函数保证不抛出任何异常。

    代码示例(Code Example):

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 #include <folly/container/SmallVector.h>
    2 #include <iostream>
    3
    4 int main() {
    5 folly::SmallVector<int, 4> sv = {10, 20, 30};
    6
    7 sv.clear(); // 清空 SmallVector
    8 std::cout << "SmallVector after clear: ";
    9 for (int val : sv) {
    10 std::cout << val << " ";
    11 }
    12 std::cout << std::endl;
    13 std::cout << "Size: " << sv.size() << std::endl;
    14 std::cout << "Capacity: " << sv.capacity() << std::endl; // 容量通常不变
    15 return 0;
    16 }

    输出 (输出可能因实现而异,但容量应该保持不变):

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 SmallVector after clear:
    2 Size: 0
    3 Capacity: 4

    要点(Key Points):
    ① 快速清空容器,时间复杂度为 \(O(n)\)(需要析构所有元素)。
    clear() 只会移除元素,不会释放已分配的内存空间,capacity() 通常保持不变。
    ③ 如果需要释放内存空间,可以考虑使用 shrink_to_fit()

    5.4.8 assign()

    函数签名(Function Signature):

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 void assign(size_type count, const value_type& value);
    2 template <class InputIterator>
    3 void assign(InputIterator first, InputIterator last);
    4 void assign(std::initializer_list<value_type> il);

    描述(Description):
    assign() 函数用新元素替换 SmallVector 的现有内容。它提供了多个重载版本,支持用多个相同元素、范围内的元素以及初始化列表中的元素进行赋值。

    参数(Parameters):
    count (size_type): 要赋值的元素数量(多个相同元素赋值版本)。
    value (const value_type&): 要赋值的元素值(多个相同元素赋值版本)。
    first (InputIterator): 输入范围的起始迭代器(范围赋值版本)。
    last (InputIterator): 输入范围的结束迭代器(范围赋值版本)。
    il (std::initializer_list<value_type>): 初始化列表(初始化列表赋值版本)。

    返回值(Return Value):
    无(None)

    异常(Exceptions):
    ⚝ 可能抛出元素拷贝过程中抛出的任何异常。
    std::bad_alloc: 如果需要重新分配内存且分配失败,可能会抛出此异常。

    代码示例(Code Example):

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 #include <folly/container/SmallVector.h>
    2 #include <vector>
    3 #include <iostream>
    4
    5 int main() {
    6 folly::SmallVector<int, 4> sv;
    7
    8 // 使用多个相同元素赋值
    9 sv.assign(3, 100); // 赋值 3 个 100
    10 std::cout << "SmallVector after multiple assign: ";
    11 for (int val : sv) {
    12 std::cout << val << " ";
    13 }
    14 std::cout << std::endl;
    15
    16 // 使用范围元素赋值
    17 std::vector<int> vec = {200, 300, 400, 500};
    18 sv.assign(vec.begin(), vec.end()); // 使用 vector 的元素赋值
    19 std::cout << "SmallVector after range assign: ";
    20 for (int val : sv) {
    21 std::cout << val << " ";
    22 }
    23 std::cout << std::endl;
    24
    25 // 使用初始化列表赋值
    26 sv.assign({600, 700}); // 使用初始化列表赋值
    27 std::cout << "SmallVector after initializer list assign: ";
    28 for (int val : sv) {
    29 std::cout << val << " ";
    30 }
    31 std::cout << std::endl;
    32
    33 return 0;
    34 }

    输出 (输出可能因实现而异,但元素赋值和顺序符合预期):

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 SmallVector after multiple assign: 100 100 100
    2 SmallVector after range assign: 200 300 400 500
    3 SmallVector after initializer list assign: 600 700

    要点(Key Points):
    ① 用新内容替换容器原有内容,时间复杂度取决于赋值元素的数量和类型。
    ② 多个重载版本提供了灵活的赋值方式。
    ③ 赋值后,容器的 size()capacity() 可能会发生变化,以适应新的元素。

    5.4.9 resize()

    函数签名(Function Signature):

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 void resize(size_type n);
    2 void resize(size_type n, const value_type& value);

    描述(Description):
    resize() 函数改变 SmallVector 的大小(size)为 n。它有两个重载版本:
    resize(n): 如果 n 小于当前 size(),则移除末尾多余的元素;如果 n 大于当前 size(),则在末尾添加默认构造的元素。
    resize(n, value): 如果 n 小于当前 size(),则移除末尾多余的元素;如果 n 大于当前 size(),则在末尾添加 value 的副本。

    参数(Parameters):
    n (size_type): 新的容器大小。
    value (const value_type&): 用于填充新增元素的默认值(可选,仅在第二个重载版本中使用)。

    返回值(Return Value):
    无(None)

    异常(Exceptions):
    ⚝ 可能抛出元素拷贝或默认构造过程中抛出的任何异常。
    std::bad_alloc: 如果需要重新分配内存且分配失败,可能会抛出此异常。

    代码示例(Code Example):

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 #include <folly/container/SmallVector.h>
    2 #include <iostream>
    3
    4 int main() {
    5 folly::SmallVector<int, 4> sv = {10, 20, 30, 40};
    6
    7 // 缩小 size
    8 sv.resize(2); // 调整 size 为 2,移除末尾元素
    9 std::cout << "SmallVector after resize(2): ";
    10 for (int val : sv) {
    11 std::cout << val << " ";
    12 }
    13 std::cout << std::endl;
    14 std::cout << "Size: " << sv.size() << std::endl;
    15
    16 // 增大 size,使用默认值填充
    17 sv.resize(5); // 调整 size 为 5,末尾添加默认构造的元素 (int 默认值为 0)
    18 std::cout << "SmallVector after resize(5): ";
    19 for (int val : sv) {
    20 std::cout << val << " ";
    21 }
    22 std::cout << std::endl;
    23 std::cout << "Size: " << sv.size() << std::endl;
    24
    25 // 增大 size,使用指定值填充
    26 sv.resize(8, 100); // 调整 size 为 8,末尾添加 100
    27 std::cout << "SmallVector after resize(8, 100): ";
    28 for (int val : sv) {
    29 std::cout << val << " ";
    30 }
    31 std::cout << std::endl;
    32 std::cout << "Size: " << sv.size() << std::endl;
    33
    34 return 0;
    35 }

    输出 (输出可能因实现而异,但元素数量和值符合预期):

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 SmallVector after resize(2): 10 20
    2 Size: 2
    3 SmallVector after resize(5): 10 20 0 0 0
    4 Size: 5
    5 SmallVector after resize(8, 100): 10 20 0 0 0 100 100 100
    6 Size: 8

    要点(Key Points):
    ① 改变容器的大小,可以增大或缩小。
    ② 增大 size 时,可以指定填充值,也可以使用默认构造值。
    ③ 缩小 size 时,会移除末尾元素,但 capacity() 通常保持不变。

    5.4.10 swap()

    函数签名(Function Signature):

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 void swap(SmallVector& other) noexcept;

    描述(Description):
    swap() 函数交换当前 SmallVector 对象与 other 对象的内容。交换操作通常非常高效,因为它只交换内部指针和容量等信息,而不需要拷贝或移动元素。

    参数(Parameters):
    other (SmallVector&): 要交换内容的另一个 SmallVector 对象。

    返回值(Return Value):
    无(None)

    异常(Exceptions):
    noexcept - 此函数保证不抛出任何异常。

    代码示例(Code Example):

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 #include <folly/container/SmallVector.h>
    2 #include <iostream>
    3
    4 int main() {
    5 folly::SmallVector<int, 4> sv1 = {10, 20};
    6 folly::SmallVector<int, 4> sv2 = {30, 40, 50};
    7
    8 std::cout << "Before swap:" << std::endl;
    9 std::cout << "sv1: "; for (int val : sv1) std::cout << val << " "; std::cout << std::endl;
    10 std::cout << "sv2: "; for (int val : sv2) std::cout << val << " "; std::cout << std::endl;
    11
    12 sv1.swap(sv2); // 交换 sv1 和 sv2 的内容
    13
    14 std::cout << "After swap:" << std::endl;
    15 std::cout << "sv1: "; for (int val : sv1) std::cout << val << " "; std::cout << std::endl;
    16 std::cout << "sv2: "; for (int val : sv2) std::cout << val << " "; std::cout << std::endl;
    17
    18 return 0;
    19 }

    输出:

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 Before swap:
    2 sv1: 10 20
    3 sv2: 30 40 50
    4 After swap:
    5 sv1: 30 40 50
    6 sv2: 10 20

    要点(Key Points):
    ① 高效交换两个 SmallVector 对象的内容,时间复杂度为 \(O(1)\)。
    swap() 操作不会使迭代器、指针和引用失效,除非它们指向被交换的容器。
    ③ 常用于实现移动赋值运算符或优化算法。

    5.5 迭代器函数详解(Iterator Functions Detailed Analysis)

    SmallVector 提供了多种迭代器函数,用于遍历容器中的元素。迭代器是访问和操作容器元素的通用接口,是 C++ 标准库容器的重要组成部分。

    5.5.1 begin()

    函数签名(Function Signature):

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 iterator begin() noexcept;
    2 const_iterator begin() const noexcept;

    描述(Description):
    begin() 函数返回指向 SmallVector 中第一个元素的迭代器。如果 SmallVector 为空,则返回的迭代器等于 end()

    参数(Parameters):
    无(None)

    返回值(Return Value):
    iterator: 指向第一个元素的迭代器(非 const 版本)。
    const_iterator: 指向第一个元素的常量迭代器(const 版本)。

    异常(Exceptions):
    noexcept - 此函数保证不抛出任何异常。

    代码示例(Code Example):

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 #include <folly/container/SmallVector.h>
    2 #include <iostream>
    3
    4 int main() {
    5 folly::SmallVector<int, 4> sv = {10, 20, 30};
    6
    7 for (auto it = sv.begin(); it != sv.end(); ++it) { // 使用 begin() 和 end() 遍历
    8 std::cout << *it << " ";
    9 }
    10 std::cout << std::endl;
    11 return 0;
    12 }

    输出:

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 10 20 30

    要点(Key Points):
    ① 返回指向容器起始位置的迭代器,用于遍历容器。
    begin() 返回的迭代器类型取决于 SmallVector 对象是否为 const
    ③ 与 end() 配合使用,可以遍历整个容器。

    5.5.2 end()

    函数签名(Function Signature):

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 iterator end() noexcept;
    2 const_iterator end() const noexcept;

    描述(Description):
    end() 函数返回指向 SmallVector 中最后一个元素之后位置的迭代器,即尾后迭代器(past-the-end iterator)。尾后迭代器本身不指向任何元素,通常用于表示遍历的结束。

    参数(Parameters):
    无(None)

    返回值(Return Value):
    iterator: 尾后迭代器(非 const 版本)。
    const_iterator: 尾后常量迭代器(const 版本)。

    异常(Exceptions):
    noexcept - 此函数保证不抛出任何异常。

    代码示例(Code Example):

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 #include <folly/container/SmallVector.h>
    2 #include <iostream>
    3
    4 int main() {
    5 folly::SmallVector<int, 4> sv = {10, 20, 30};
    6
    7 for (auto it = sv.begin(); it != sv.end(); ++it) { // 使用 begin() 和 end() 遍历
    8 std::cout << *it << " ";
    9 }
    10 std::cout << std::endl;
    11 return 0;
    12 }

    输出:

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 10 20 30

    要点(Key Points):
    ① 返回指向容器末尾之后位置的迭代器,用于判断遍历是否结束。
    end() 返回的迭代器类型取决于 SmallVector 对象是否为 const
    ③ 尾后迭代器不能解引用。

    5.5.3 cbegin()

    函数签名(Function Signature):

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 const_iterator cbegin() const noexcept;

    描述(Description):
    cbegin() 函数返回指向 SmallVector 中第一个元素的常量迭代器(const_iterator)。即使 SmallVector 对象本身不是 constcbegin() 也总是返回常量迭代器,用于只读遍历。

    参数(Parameters):
    无(None)

    返回值(Return Value):
    const_iterator: 指向第一个元素的常量迭代器。

    异常(Exceptions):
    noexcept - 此函数保证不抛出任何异常。

    代码示例(Code Example):

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 #include <folly/container/SmallVector.h>
    2 #include <iostream>
    3
    4 int main() {
    5 folly::SmallVector<int, 4> sv = {10, 20, 30};
    6
    7 for (auto it = sv.cbegin(); it != sv.cend(); ++it) { // 使用 cbegin() 和 cend() 遍历
    8 std::cout << *it << " "; // 只能读取元素,不能修改
    9 // *it = 100; // 错误:常量迭代器不能修改元素
    10 }
    11 std::cout << std::endl;
    12 return 0;
    13 }

    输出:

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 10 20 30

    要点(Key Points):
    ① 返回常量迭代器,用于只读遍历,保证不会意外修改元素。
    cbegin() 总是返回 const_iterator,即使对非 const 对象调用。
    ③ 与 cend() 配合使用,进行只读遍历。

    5.5.4 cend()

    函数签名(Function Signature):

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 const_iterator cend() const noexcept;

    描述(Description):
    cend() 函数返回指向 SmallVector 中最后一个元素之后位置的常量迭代器(const_iterator),即尾后常量迭代器(const_past-the-end iterator)。即使 SmallVector 对象本身不是 constcend() 也总是返回常量迭代器,用于只读遍历。

    参数(Parameters):
    无(None)

    返回值(Return Value):
    const_iterator: 尾后常量迭代器。

    异常(Exceptions):
    noexcept - 此函数保证不抛出任何异常。

    代码示例(Code Example):

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 #include <folly/container/SmallVector.h>
    2 #include <iostream>
    3
    4 int main() {
    5 folly::SmallVector<int, 4> sv = {10, 20, 30};
    6
    7 for (auto it = sv.cbegin(); it != sv.cend(); ++it) { // 使用 cbegin() 和 cend() 遍历
    8 std::cout << *it << " ";
    9 }
    10 std::cout << std::endl;
    11 return 0;
    12 }

    输出:

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 10 20 30

    要点(Key Points):
    ① 返回尾后常量迭代器,用于只读遍历的结束判断。
    cend() 总是返回 const_iterator,即使对非 const 对象调用。
    ③ 尾后常量迭代器不能解引用。

    5.5.5 rbegin()

    函数签名(Function Signature):

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 reverse_iterator rbegin() noexcept;
    2 const_reverse_iterator rbegin() const noexcept;

    描述(Description):
    rbegin() 函数返回指向 SmallVector 中最后一个元素的反向迭代器(reverse_iterator)。反向迭代器从容器的末尾开始向前遍历。

    参数(Parameters):
    无(None)

    返回值(Return Value):
    reverse_iterator: 指向最后一个元素的反向迭代器(非 const 版本)。
    const_reverse_iterator: 指向最后一个元素的常量反向迭代器(const 版本)。

    异常(Exceptions):
    noexcept - 此函数保证不抛出任何异常。

    代码示例(Code Example):

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 #include <folly/container/SmallVector.h>
    2 #include <iostream>
    3
    4 int main() {
    5 folly::SmallVector<int, 4> sv = {10, 20, 30};
    6
    7 for (auto it = sv.rbegin(); it != sv.rend(); ++it) { // 使用 rbegin() 和 rend() 反向遍历
    8 std::cout << *it << " ";
    9 }
    10 std::cout << std::endl;
    11 return 0;
    12 }

    输出:

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 30 20 10

    要点(Key Points):
    ① 返回反向迭代器,用于从后向前遍历容器。
    rbegin() 返回的迭代器类型取决于 SmallVector 对象是否为 const
    ③ 与 rend() 配合使用,进行反向遍历。

    5.5.6 rend()

    函数签名(Function Signature):

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 reverse_iterator rend() noexcept;
    2 const_reverse_iterator rend() const noexcept;

    描述(Description):
    rend() 函数返回指向 SmallVector 中第一个元素之前位置的反向迭代器(reverse_iterator),即反向尾后迭代器(reverse_past-the-end iterator)。反向尾后迭代器本身不指向任何元素,通常用于表示反向遍历的结束。

    参数(Parameters):
    无(None)

    返回值(Return Value):
    reverse_iterator: 反向尾后迭代器(非 const 版本)。
    const_reverse_iterator: 反向尾后常量迭代器(const 版本)。

    异常(Exceptions):
    noexcept - 此函数保证不抛出任何异常。

    代码示例(Code Example):

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 #include <folly/container/SmallVector.h>
    2 #include <iostream>
    3
    4 int main() {
    5 folly::SmallVector<int, 4> sv = {10, 20, 30};
    6
    7 for (auto it = sv.rbegin(); it != sv.rend(); ++it) { // 使用 rbegin() 和 rend() 反向遍历
    8 std::cout << *it << " ";
    9 }
    10 std::cout << std::endl;
    11 return 0;
    12 }

    输出:

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 30 20 10

    要点(Key Points):
    ① 返回反向尾后迭代器,用于反向遍历的结束判断。
    rend() 返回的迭代器类型取决于 SmallVector 对象是否为 const
    ③ 反向尾后迭代器不能解引用。

    5.5.7 crbegin()

    函数签名(Function Signature):

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 const_reverse_iterator crbegin() const noexcept;

    描述(Description):
    crbegin() 函数返回指向 SmallVector 中最后一个元素的常量反向迭代器(const_reverse_iterator)。即使 SmallVector 对象本身不是 constcrbegin() 也总是返回常量反向迭代器,用于只读反向遍历。

    参数(Parameters):
    无(None)

    返回值(Return Value):
    const_reverse_iterator: 指向最后一个元素的常量反向迭代器。

    异常(Exceptions):
    noexcept - 此函数保证不抛出任何异常。

    代码示例(Code Example):

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 #include <folly/container/SmallVector.h>
    2 #include <iostream>
    3
    4 int main() {
    5 folly::SmallVector<int, 4> sv = {10, 20, 30};
    6
    7 for (auto it = sv.crbegin(); it != sv.crend(); ++it) { // 使用 crbegin() 和 crend() 反向只读遍历
    8 std::cout << *it << " "; // 只能读取元素,不能修改
    9 // *it = 100; // 错误:常量反向迭代器不能修改元素
    10 }
    11 std::cout << std::endl;
    12 return 0;
    13 }

    输出:

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 30 20 10

    要点(Key Points):
    ① 返回常量反向迭代器,用于只读反向遍历,保证不会意外修改元素。
    crbegin() 总是返回 const_reverse_iterator,即使对非 const 对象调用。
    ③ 与 crend() 配合使用,进行只读反向遍历。

    5.5.8 crend()

    函数签名(Function Signature):

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 const_reverse_iterator crend() const noexcept;

    描述(Description):
    crend() 函数返回指向 SmallVector 中第一个元素之前位置的常量反向迭代器(const_reverse_iterator),即反向尾后常量迭代器(const_reverse_past-the-end iterator)。即使 SmallVector 对象本身不是 constcrend() 也总是返回常量反向迭代器,用于只读反向遍历。

    参数(Parameters):
    无(None)

    返回值(Return Value):
    const_reverse_iterator: 反向尾后常量迭代器。

    异常(Exceptions):
    noexcept - 此函数保证不抛出任何异常。

    代码示例(Code Example):

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 #include <folly/container/SmallVector.h>
    2 #include <iostream>
    3
    4 int main() {
    5 folly::SmallVector<int, 4> sv = {10, 20, 30};
    6
    7 for (auto it = sv.crbegin(); it != sv.crend(); ++it) { // 使用 crbegin() 和 crend() 反向只读遍历
    8 std::cout << *it << " ";
    9 }
    10 std::cout << std::endl;
    11 return 0;
    12 }

    输出:

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 30 20 10

    要点(Key Points):
    ① 返回反向尾后常量迭代器,用于只读反向遍历的结束判断。
    crend() 总是返回 const_reverse_iterator,即使对非 const 对象调用。
    ③ 反向尾后常量迭代器不能解引用。

    END_OF_CHAPTER

    6. chapter 6: SmallVector 常见问题与最佳实践(SmallVector Common Problems and Best Practices)

    6.1 常见错误用法与陷阱(Common Misuse and Pitfalls):避免 SmallVector 使用中的常见错误

    SmallVector 作为一种高效且灵活的容器,在许多场景下都能提供优异的性能。然而,不当的使用方式可能会导致性能下降,甚至引入难以调试的错误。本节将深入探讨 SmallVector 常见的错误用法与陷阱,帮助读者避免这些问题,写出更健壮、更高效的代码。

    6.1.1 未充分理解 Capacity 模板参数的影响(Insufficient Understanding of Capacity Template Parameter)

    SmallVector 最核心的特性之一就是其固定的栈上容量 Capacity。初学者最容易犯的错误就是没有充分理解 Capacity 模板参数的意义,导致在不合适的场景下使用了 SmallVector,或者设置了不合理的 Capacity 值。

    误用场景:大规模数据存储
    SmallVector 的设计初衷是为了处理小规模数据,当数据规模超过 Capacity 时,SmallVector 会退化为在堆上分配内存,此时其性能优势会大打折扣。如果需要存储大规模数据,std::vectorFBVector 等动态数组才是更合适的选择。

    Capacity 设置过小
    如果 Capacity 设置得过小,频繁超出栈上容量会导致 SmallVector 频繁地进行堆内存分配和数据拷贝,这会显著降低性能,甚至比 std::vector 更慢。

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 #include <folly/container/SmallVector.h>
    2 #include <iostream>
    3 #include <chrono>
    4 #include <vector>
    5
    6 using namespace folly;
    7 using namespace std;
    8 using namespace std::chrono;
    9
    10 int main() {
    11 // Capacity 设置过小的 SmallVector
    12 SmallVector<int, 4> sv_small_capacity;
    13 vector<int> vec;
    14
    15 auto start_sv = high_resolution_clock::now();
    16 for (int i = 0; i < 10000; ++i) {
    17 sv_small_capacity.push_back(i); // 频繁堆分配
    18 }
    19 auto end_sv = high_resolution_clock::now();
    20 auto duration_sv = duration_cast<microseconds>(end_sv - start_sv);
    21
    22 auto start_vec = high_resolution_clock::now();
    23 for (int i = 0; i < 10000; ++i) {
    24 vec.push_back(i);
    25 }
    26 auto end_vec = high_resolution_clock::now();
    27 auto duration_vec = duration_cast<microseconds>(end_vec - start_vec);
    28
    29 cout << "SmallVector (small capacity) push_back duration: " << duration_sv.count() << " microseconds" << endl;
    30 cout << "std::vector push_back duration: " << duration_vec.count() << " microseconds" << endl;
    31
    32 return 0;
    33 }

    在这个例子中,SmallVectorCapacity 设置为 4,当循环次数达到 10000 时,SmallVector 会频繁进行堆分配,导致性能下降。std::vector 由于其动态扩容机制,性能表现反而更好。

    Capacity 设置过大
    虽然 Capacity 设置过小会频繁触发堆分配,但 Capacity 也不是越大越好。过大的 Capacity 会占用更多的栈空间。栈空间通常是有限的资源,过量占用栈空间可能导致栈溢出(Stack Overflow)的风险,尤其是在递归调用或多线程环境下。此外,即使没有栈溢出,过大的栈上对象也会增加函数调用的开销,因为函数调用时需要在栈上分配空间。

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 #include <folly/container/SmallVector.h>
    2 #include <iostream>
    3
    4 using namespace folly;
    5 using namespace std;
    6
    7 void large_small_vector() {
    8 // Capacity 设置过大的 SmallVector
    9 SmallVector<int, 1024 * 1024> sv_large_capacity; // 占用 4MB 栈空间 (假设 int 4 字节)
    10 cout << "Large SmallVector created on stack." << endl;
    11 }
    12
    13 int main() {
    14 large_small_vector();
    15 return 0;
    16 }

    如果 large_small_vector 函数被频繁调用,或者在栈空间有限的环境下,就可能引发栈溢出问题。

    最佳实践
    评估数据规模:在使用 SmallVector 之前,务必评估你的数据规模。如果数据规模通常较小且可预测,SmallVector 是一个很好的选择。如果数据规模波动较大或可能很大,std::vector 或其他动态容器可能更合适。
    合理设置 Capacity:根据实际应用场景,选择一个合适的 Capacity 值。Capacity 应该足够大,以容纳绝大多数情况下的数据,同时又不能过大,以免浪费栈空间。可以通过性能测试和 профилирование (profiling) 来找到最佳的 Capacity 值。
    考虑使用 constexpr Capacity:如果 Capacity 在编译时是已知的,可以使用 constexpr 来定义 Capacity,这样可以进一步提升性能,并允许编译器进行更多的优化。

    6.1.2 迭代器失效问题(Iterator Invalidation Issues)

    std::vector 类似,SmallVector 在进行某些操作时,也可能导致迭代器失效。理解迭代器失效的场景,并编写避免迭代器失效的代码,是使用 SmallVector 的重要一环。

    插入元素导致迭代器失效
    当向 SmallVector 中插入元素时,如果插入位置之前的元素数量加上插入元素数量超过了 Capacity,SmallVector 可能会发生内存重分配(从栈上到堆上,或者堆上重新分配更大的空间)。内存重分配会导致原有内存块失效,因此指向原有内存块的迭代器也会失效。即使没有内存重分配,在 insert 操作之后,插入位置及其后的所有元素的迭代器都可能失效,因为元素的位置发生了移动。

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 #include <folly/container/SmallVector.h>
    2 #include <iostream>
    3
    4 using namespace folly;
    5 using namespace std;
    6
    7 int main() {
    8 SmallVector<int, 4> sv = {1, 2, 3, 4};
    9 auto it = sv.begin();
    10 ++it; // 指向元素 2
    11
    12 sv.insert(sv.begin(), 0); // 在头部插入元素,可能导致迭代器失效
    13
    14 // 错误用法:使用可能失效的迭代器
    15 // cout << *it << endl; // 可能崩溃或输出错误结果
    16
    17 // 正确用法:重新获取迭代器
    18 it = sv.begin();
    19 ++it;
    20 cout << *it << endl; // 正确输出 2
    21
    22 return 0;
    23 }

    insert 操作之后,之前的迭代器 it 可能已经失效。尝试解引用失效的迭代器会导致未定义行为。正确的做法是在 insert 操作后,重新获取迭代器。

    删除元素导致迭代器失效
    从 SmallVector 中删除元素,特别是使用 erase 函数删除元素时,被删除元素及其后的所有元素的迭代器都会失效。

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 #include <folly/container/SmallVector.h>
    2 #include <iostream>
    3
    4 using namespace folly;
    5 using namespace std;
    6
    7 int main() {
    8 SmallVector<int, 4> sv = {1, 2, 3, 4, 5};
    9 auto it = sv.begin();
    10 ++it; // 指向元素 2
    11
    12 sv.erase(sv.begin()); // 删除头部元素,导致迭代器失效
    13
    14 // 错误用法:使用可能失效的迭代器
    15 // cout << *it << endl; // 可能崩溃或输出错误结果
    16
    17 // 正确用法:重新获取迭代器或使用 erase 的返回值
    18 it = sv.begin(); // 重新获取迭代器
    19 cout << *it << endl; // 正确输出 2
    20
    21 // 或者使用 erase 的返回值,erase 返回指向被删除元素之后元素的迭代器
    22 sv = {1, 2, 3, 4, 5};
    23 it = sv.begin();
    24 ++it;
    25 it = sv.erase(sv.begin()); // it 指向原元素 2 的位置,现在是元素 2 的下一个元素,即原元素 3
    26
    27 cout << *it << endl; // 正确输出 3 (如果容器不为空)
    28
    29 return 0;
    30 }

    erase 操作会使被删除元素及其后的元素的迭代器失效。为了安全地继续使用迭代器,可以重新获取迭代器,或者使用 erase 函数的返回值,它返回指向被删除元素之后元素的有效迭代器。

    最佳实践
    谨慎使用迭代器:在进行可能导致迭代器失效的操作(如 insert, erase, push_back 等)之后,不要继续使用之前的迭代器,除非你非常清楚迭代器仍然有效。
    使用基于索引的循环:在某些情况下,可以使用基于索引的循环来代替迭代器循环,这样可以避免迭代器失效的问题。但是,基于索引的循环可能不如迭代器循环灵活。
    关注 API 文档:仔细阅读 SmallVector 的 API 文档,了解哪些操作可能导致迭代器失效,以及失效的具体规则。

    6.1.3 拷贝与移动语义的误用(Misuse of Copy and Move Semantics)

    C++11 引入了移动语义,旨在减少不必要的拷贝操作,提升性能。SmallVector 也充分利用了移动语义。然而,如果对拷贝和移动语义理解不足,可能会导致性能下降,甚至产生逻辑错误。

    不必要的拷贝
    在某些情况下,本可以进行移动操作的场景,却错误地进行了拷贝操作,导致性能损失。例如,当向 SmallVector 中插入一个临时对象时,应该使用移动语义,但如果代码写成了拷贝语义,就会产生额外的开销。

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 #include <folly/container/SmallVector.h>
    2 #include <iostream>
    3 #include <string>
    4
    5 using namespace folly;
    6 using namespace std;
    7
    8 struct MyString {
    9 string data;
    10 MyString(const string& s) : data(s) {
    11 cout << "MyString copy constructor called" << endl;
    12 }
    13 MyString(string&& s) : data(move(s)) {
    14 cout << "MyString move constructor called" << endl;
    15 }
    16 };
    17
    18 int main() {
    19 SmallVector<MyString, 4> sv;
    20 string temp_str = "hello";
    21
    22 cout << "Pushing back a temporary string (copy):" << endl;
    23 sv.push_back(MyString(temp_str)); // 调用拷贝构造函数
    24
    25 cout << "\nPushing back a temporary string (move):" << endl;
    26 sv.emplace_back(temp_str); // 调用移动构造函数 (in-place 构造)
    27
    28 return 0;
    29 }

    在第一个 push_back 中,由于使用了 MyString(temp_str) 显式构造了一个临时对象,但 push_back 接收的是左值引用,因此会调用拷贝构造函数。而在 emplace_back 中,直接在 SmallVector 内部构造对象,并使用了移动语义,避免了拷贝。

    移动后对象的状态
    移动操作会将源对象的状态转移到目标对象,移动后的源对象通常处于有效但未指定的状态。这意味着,移动后的源对象仍然可以析构,但不能再安全地访问其内容。如果错误地使用了移动后的源对象,可能会导致未定义行为。

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 #include <folly/container/SmallVector.h>
    2 #include <iostream>
    3 #include <string>
    4
    5 using namespace folly;
    6 using namespace std;
    7
    8 int main() {
    9 SmallVector<string, 4> sv1 = {"hello", "world"};
    10 SmallVector<string, 4> sv2 = move(sv1); // 移动 sv1 到 sv2
    11
    12 // 错误用法:访问移动后的 sv1
    13 // cout << sv1[0] << endl; // 未定义行为
    14
    15 cout << "sv2[0]: " << sv2[0] << endl; // 正确访问 sv2
    16
    17 return 0;
    18 }

    在移动操作 move(sv1) 后,sv1 的状态变得未指定,访问 sv1 的元素是未定义行为。应该只访问移动后的目标对象 sv2

    最佳实践
    理解拷贝与移动语义:深入理解 C++ 的拷贝构造函数、移动构造函数、拷贝赋值运算符和移动赋值运算符,以及它们在不同场景下的调用时机。
    优先使用移动语义:在可以进行移动操作的场景下,优先使用移动语义,例如使用 std::move 显式移动,或者使用 emplace_back 等 in-place 构造函数。
    避免使用移动后的源对象:在移动操作后,不要再访问源对象的内容,除非你明确知道源对象的状态是可用的。

    6.1.4 异常安全性的考虑(Exception Safety Considerations)

    异常安全性是编写健壮 C++ 代码的重要方面。SmallVector 在设计时考虑了异常安全性,但开发者在使用 SmallVector 时,仍然需要注意异常安全编程的实践。

    强异常安全保证
    SmallVector 的某些操作,例如 push_back, emplace_back, insert 等,在分配内存或构造元素时,可能会抛出异常。在这些操作中,SmallVector 通常提供强异常安全保证,即如果操作抛出异常,程序的状态不会发生改变("commit or rollback")。例如,如果 push_back 在分配内存时抛出异常,SmallVector 的状态仍然保持不变,之前 push_back 的元素不会被添加到 SmallVector 中。

    基本异常安全保证
    SmallVector 的另一些操作,例如 erase, pop_back, clear 等,通常提供基本异常安全保证,即如果操作抛出异常,程序不会崩溃,资源不会泄漏,但程序的状态可能发生改变。例如,如果 erase 操作在删除元素的过程中抛出异常,SmallVector 的状态可能是不一致的,但程序仍然可以继续运行。

    noexcept 操作
    SmallVector 的某些操作被标记为 noexcept,例如移动构造函数、移动赋值运算符、析构函数等。这意味着这些操作保证不会抛出异常。noexcept 操作对于编写异常安全的代码非常重要,因为它可以简化异常处理逻辑,并允许编译器进行更多的优化。

    最佳实践
    了解异常安全级别:了解 SmallVector 各个 API 的异常安全级别(强异常安全、基本异常安全、noexcept),以便在编写代码时做出正确的异常处理决策。
    RAII 原则:遵循 RAII (Resource Acquisition Is Initialization) 原则,使用对象来管理资源,确保资源在异常情况下也能被正确释放。SmallVector 本身就是一个 RAII 容器,它负责管理其内部的内存资源。
    编写异常安全的代码:在编写使用 SmallVector 的代码时,要考虑异常情况,并编写异常安全的代码,例如使用 try-catch 块捕获和处理异常,避免资源泄漏和程序崩溃。

    6.2 性能调优技巧(Performance Tuning Tips):提升 SmallVector 应用性能的实用技巧

    SmallVector 的设计目标之一就是高性能。为了充分发挥 SmallVector 的性能优势,开发者需要掌握一些性能调优技巧。本节将介绍一些实用的 SmallVector 性能调优技巧,帮助读者写出更高效的代码。

    6.2.1 合理预估并设置 Capacity(Estimating and Setting Capacity Appropriately)

    如前所述,Capacity 模板参数是 SmallVector 性能的关键。合理预估并设置 Capacity 是性能调优的首要步骤。

    统计分析
    通过统计分析实际应用场景中的数据规模,可以预估 SmallVector 需要的 Capacity。例如,如果已知在 99% 的情况下,数据规模不会超过 16,那么可以将 Capacity 设置为 16。

    профилирование (Profiling)
    使用性能 профилирование 工具(如 Google Performance Tools, Valgrind 等)来分析程序的性能瓶颈。通过 профилирование,可以了解 SmallVector 的内存分配情况,以及是否频繁发生堆分配。根据 профилирование 结果,调整 Capacity 值,直到性能达到最佳。

    动态调整 Capacity (谨慎使用)
    在某些特殊情况下,如果无法预先确定最佳 Capacity,可以考虑动态调整 Capacity。例如,可以先使用一个较小的 Capacity,当 SmallVector 发生堆分配时,再动态地增加 Capacity。但是,动态调整 Capacity 会增加代码的复杂性,并且可能引入新的性能问题,因此需要谨慎使用。

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 #include <folly/container/SmallVector.h>
    2 #include <iostream>
    3
    4 using namespace folly;
    5 using namespace std;
    6
    7 template <typename T, size_t InitialCapacity>
    8 class DynamicCapacitySmallVector : public SmallVector<T, InitialCapacity> {
    9 public:
    10 using Base = SmallVector<T, InitialCapacity>;
    11 using Base::Base;
    12
    13 void push_back(const T& value) {
    14 if (this->full()) {
    15 // 动态增加 Capacity (例如翻倍)
    16 this->reserve(this->capacity() * 2); // 堆分配
    17 }
    18 Base::push_back(value);
    19 }
    20
    21 void push_back(T&& value) {
    22 if (this->full()) {
    23 this->reserve(this->capacity() * 2);
    24 }
    25 Base::push_back(move(value));
    26 }
    27 };
    28
    29 int main() {
    30 DynamicCapacitySmallVector<int, 4> dyn_sv;
    31 for (int i = 0; i < 100; ++i) {
    32 dyn_sv.push_back(i); // 初始 Capacity 为 4,超出后动态扩容
    33 }
    34 cout << "DynamicCapacitySmallVector size: " << dyn_sv.size() << ", capacity: " << dyn_sv.capacity() << endl;
    35
    36 return 0;
    37 }

    DynamicCapacitySmallVector 类继承自 SmallVector,并在 push_back 操作中动态地增加 Capacity。这种方法可以应对数据规模不确定的情况,但需要权衡性能和复杂性。

    最佳实践
    优先静态 Capacity:尽可能在编译时确定 Capacity,并使用静态 Capacity 的 SmallVector。
    合理预估 Capacity:通过统计分析和 профилирование,合理预估并设置 Capacity
    谨慎动态 Capacity:只有在必要时才考虑动态调整 Capacity,并仔细评估其性能影响。

    6.2.2 使用移动语义减少拷贝(Using Move Semantics to Reduce Copies)

    移动语义是提升 SmallVector 性能的有效手段。在元素插入、赋值等操作中,尽可能使用移动语义,可以减少不必要的拷贝开销。

    emplace_backemplace
    使用 emplace_backemplace 函数进行元素插入,可以直接在 SmallVector 内部构造元素,避免临时对象的产生和拷贝。

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 #include <folly/container/SmallVector.h>
    2 #include <iostream>
    3 #include <string>
    4
    5 using namespace folly;
    6 using namespace std;
    7
    8 struct MyObject {
    9 string data;
    10 MyObject(const string& s) : data(s) {
    11 cout << "MyObject copy constructor" << endl;
    12 }
    13 MyObject(string&& s) : data(move(s)) {
    14 cout << "MyObject move constructor" << endl;
    15 }
    16 };
    17
    18 int main() {
    19 SmallVector<MyObject, 4> sv;
    20 string temp_str = "hello";
    21
    22 cout << "push_back (copy):" << endl;
    23 sv.push_back(MyObject(temp_str)); // 拷贝构造
    24
    25 cout << "\nemplace_back (move):" << endl;
    26 sv.emplace_back(temp_str); // 移动构造 (in-place)
    27
    28 return 0;
    29 }

    emplace_back 直接在 SmallVector 内部构造 MyObject 对象,调用的是移动构造函数,避免了拷贝。

    std::move
    在将对象插入或赋值给 SmallVector 时,可以使用 std::move 将对象转换为右值引用,强制进行移动操作。

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 #include <folly/container/SmallVector.h>
    2 #include <iostream>
    3 #include <string>
    4
    5 using namespace folly;
    6 using namespace std;
    7
    8 int main() {
    9 SmallVector<string, 4> sv1 = {"hello"};
    10 SmallVector<string, 4> sv2;
    11
    12 cout << "Copy assignment:" << endl;
    13 sv2 = sv1; // 拷贝赋值
    14
    15 cout << "\nMove assignment:" << endl;
    16 SmallVector<string, 4> sv3;
    17 sv3 = move(sv1); // 移动赋值
    18
    19 return 0;
    20 }

    move(sv1)sv1 转换为右值引用,使得赋值操作 sv3 = move(sv1) 调用移动赋值运算符,而不是拷贝赋值运算符。

    最佳实践
    优先使用 emplace_backemplace:在插入元素时,尽可能使用 emplace_backemplace
    显式使用 std::move:在需要移动语义的场景下,显式使用 std::move
    自定义类型的移动语义:对于自定义类型,确保正确实现移动构造函数和移动赋值运算符,以便 SmallVector 可以充分利用移动语义。

    6.2.3 避免不必要的内存分配(Avoiding Unnecessary Memory Allocations)

    内存分配是昂贵的操作。SmallVector 的栈上存储特性可以减少堆内存分配,但仍然需要注意避免不必要的内存分配。

    reserve 预分配空间
    如果预先知道 SmallVector 大概需要存储多少元素,可以使用 reserve 函数预先分配足够的空间。reserve 可以减少因容量不足而导致的内存重分配次数。

    1.双击鼠标左键复制此行;2.单击复制所有代码。
                                    
                                        
    1 #include <folly/container/SmallVector.h>
    2 #include <iostream>
    3 #include <vector>
    4 #include <chrono>
    5
    6 using namespace folly;
    7 using namespace std;
    8 using namespace std::chrono;
    9
    10 int main() {
    11 // 不使用 reserve
    12 SmallVector<int, 16> sv1;
    13 auto start1 = high_resolution_clock::now();
    14 for (int i = 0; i < 1000; ++i) {
    15 sv1.push_back(i);
    16 }
    17 auto end1 = high_resolution_clock::now();
    18 auto duration1 = duration_cast<microseconds>(end1 - start1);
    19
    20 // 使用 reserve 预分配空间
    21 SmallVector<int, 16> sv2;
    22 sv2.reserve(1000); // 预分配 1000 个元素的空间 (堆上)
    23 auto start2 = high_resolution_clock::now();
    24 for (int i = 0; i < 1000; ++i) {
    25 sv2.push_back(i);
    26 }
    27 auto end2 = high_resolution_clock::now();
    28 auto duration2 = duration_cast<microseconds>(end2 - start2);
    29
    30 cout << "Without reserve duration: " << duration1.count() << " microseconds" << endl;
    31 cout << "With reserve duration: " << duration2.count() << " microseconds" << endl;
    32
    33 return 0;
    34 }

    在循环插入大量元素之前,使用 reserve 预分配空间,可以显著减少内存重分配的次数,提升性能。

    避免频繁的 inserterase 操作
    inserterase 操作通常需要移动元素,如果频繁进行这些操作,会产生额外的开销。在性能敏感的场景下,应尽量避免频繁的 inserterase 操作。如果需要频繁进行插入和删除操作,可以考虑使用 std::dequestd::list 等其他容器。

    shrink_to_fit 释放多余空间
    当 SmallVector 的容量远大于实际元素数量时,可以使用 shrink_to_fit 函数释放多余的堆内存空间。shrink_to_fit 可以减少内存占用,但可能会导致内存重分配,因此需要权衡使用。

    最佳实践
    使用 reserve 预分配空间:在预知数据规模的情况下,使用 reserve 预分配空间。
    减少 inserterase 操作:尽量避免频繁的 inserterase 操作。
    谨慎使用 shrink_to_fit:在内存敏感的场景下,可以考虑使用 shrink_to_fit 释放多余空间,但需要注意其性能影响。

    6.2.4 自定义分配器 (Custom Allocator) (高级技巧)

    对于高级用户,可以考虑使用自定义分配器 (Custom Allocator) 来进一步优化 SmallVector 的内存管理。自定义分配器可以实现更精细的内存控制,例如使用内存池、共享内存等。

    内存池分配器
    内存池分配器可以预先分配一大块内存,然后从中分配小块内存。内存池分配器可以减少内存分配和释放的开销,提高内存分配效率。

    共享内存分配器
    在多进程或跨进程通信的场景下,可以使用共享内存分配器,将 SmallVector 的数据存储在共享内存中,实现进程间的数据共享。

    最佳实践
    了解自定义分配器:深入了解 C++ 的自定义分配器机制,以及其在内存管理中的作用。
    根据场景选择分配器:根据实际应用场景,选择合适的自定义分配器,例如内存池分配器、共享内存分配器等。
    谨慎使用自定义分配器:自定义分配器是高级技巧,使用不当可能会引入新的问题。只有在对内存管理有深入理解,并且性能瓶颈确实在内存分配时,才考虑使用自定义分配器。

    6.3 与其他容器的选择:std::vector, std::array, std::deque 等(Choosing between SmallVector and other containers: std::vector, std::array, std::deque, etc.):根据不同场景选择最合适的容器

    C++ 标准库提供了多种容器,每种容器都有其特定的适用场景。SmallVector 虽然在某些场景下具有优势,但并非万能容器。本节将 SmallVector 与 std::vector, std::array, std::deque 等常用容器进行对比,帮助读者根据不同场景选择最合适的容器。

    6.3.1 SmallVector vs std::vector

    std::vector 是 C++ 标准库中最常用的动态数组容器。SmallVector 与 std::vector 都是动态数组,但在内存分配策略上有所不同。

    特性SmallVectorstd::vector
    内存分配栈上 + 堆上堆上
    容量编译时固定栈上容量,运行时动态堆容量运行时动态堆容量
    性能小规模数据栈上分配,性能高;大规模数据堆分配,性能接近 std::vector堆分配,性能稳定,但小规模数据可能略逊于 SmallVector
    适用场景小规模数据为主,偶尔有大规模数据的场景大规模数据为主,数据规模波动较大的场景
    栈溢出风险有,Capacity 过大可能导致栈溢出
    编译时容量优化可以通过 constexpr Capacity 进行编译时优化

    选择建议
    优先 SmallVector (小规模数据):如果数据规模通常较小(例如几十个元素以内),且对性能要求较高,优先选择 SmallVector。
    std::vector (大规模数据):如果数据规模较大或波动较大,或者不确定数据规模,选择 std::vector 更安全可靠。
    混合使用:在某些复杂场景下,可以混合使用 SmallVector 和 std::vector。例如,可以使用 SmallVector 作为局部变量,处理小规模数据;使用 std::vector 作为类成员变量,处理大规模数据。

    6.3.2 SmallVector vs std::array

    std::array 是 C++ 标准库中的固定大小数组容器。std::array 的大小在编译时确定,存储在栈上,与 C 风格数组类似,但提供了更安全的接口和标准库容器的便利性。

    特性SmallVectorstd::array
    内存分配栈上 + 堆上栈上
    容量编译时固定栈上容量,运行时动态堆容量编译时固定大小
    大小可变性运行时可动态增长 (超出 Capacity 后)编译时固定大小,不可动态增长
    性能小规模数据栈上分配,性能高;可动态增长栈上分配,性能极高,编译时大小确定
    适用场景小规模数据为主,偶尔需要动态增长的场景编译时大小已知,不需要动态增长的场景
    栈溢出风险有,Capacity 过大可能导致栈溢出有,std::array 大小过大可能导致栈溢出
    灵活性std::array 更灵活,可动态增长灵活性较低,大小固定

    选择建议
    优先 std::array (固定大小):如果数组大小在编译时已知,且不需要动态增长,优先选择 std::arraystd::array 的性能通常是最高的,且类型安全。
    SmallVector (小规模动态):如果数据规模通常较小,但偶尔需要动态增长,选择 SmallVector。
    避免混淆std::array 的大小是模板参数,必须在编译时确定;SmallVector 的 Capacity 也是模板参数,但超出 Capacity 后可以动态增长。不要将两者混淆。

    6.3.3 SmallVector vs std::deque

    std::deque (双端队列) 是一种动态数组容器,支持在头部和尾部高效地插入和删除元素。std::deque 的内存分配策略与 std::vector 和 SmallVector 不同,std::deque 通常由多个独立的内存块组成。

    特性SmallVectorstd::deque
    内存分配栈上 + 堆上多个堆内存块
    容量编译时固定栈上容量,运行时动态堆容量运行时动态增长,分块分配
    插入/删除尾部高效插入/删除,中间插入/删除可能较慢头部和尾部高效插入/删除,中间插入/删除可能较慢
    内存连续性栈上或堆上连续内存内存不保证连续
    迭代器失效插入/删除可能导致迭代器失效头部或尾部插入/删除可能导致迭代器失效
    适用场景小规模数据为主,偶尔有大规模数据,尾部操作为主需要在头部和尾部频繁插入/删除元素的场景
    性能小规模数据栈上分配,性能高;尾部操作高效头部和尾部操作高效,但随机访问性能可能略逊于 vector

    选择建议
    SmallVector (小规模尾部操作):如果数据规模通常较小,且主要在尾部进行插入和删除操作,可以选择 SmallVector。
    std::deque (双端操作):如果需要在头部和尾部频繁进行插入和删除操作,std::deque 是更合适的选择。
    std::vector (随机访问):如果需要频繁进行随机访问,且插入和删除操作较少,std::vector 通常是更好的选择,因为 std::vector 的内存是连续的,随机访问性能更高。

    6.3.4 其他容器

    除了 std::vector, std::array, std::deque 之外,C++ 标准库还提供了其他容器,例如 std::list, std::forward_list, std::set, std::map 等。这些容器各有特点,适用于不同的场景。

    std::liststd::forward_list (链表):适用于频繁在任意位置插入和删除元素的场景,但随机访问性能较差。
    std::setstd::map (关联容器):适用于需要存储键值对,并进行快速查找、插入和删除操作的场景,元素自动排序。
    std::unordered_setstd::unordered_map (哈希表):适用于需要快速查找、插入和删除操作的场景,元素无序,平均查找时间为常数时间。

    通用选择原则
    根据需求选择:根据实际应用场景的需求,例如数据规模、操作类型(插入、删除、查找、随机访问等)、性能要求、内存限制等,选择最合适的容器。
    性能 профилирование (Profiling):在性能敏感的场景下,使用性能 профилирование 工具来评估不同容器的性能,选择性能最佳的容器。
    代码可读性和维护性:在满足性能需求的前提下,也要考虑代码的可读性和维护性。选择最易于理解和维护的容器。

    6.4 SmallVector 的未来发展趋势(Future Development Trends of SmallVector):展望 SmallVector 的发展方向和潜在改进

    SmallVector 作为 Folly 库中的重要组件,在现代 C++ 开发中发挥着越来越重要的作用。随着 C++ 标准的不断发展和应用场景的日益复杂,SmallVector 也在不断演进和完善。本节将展望 SmallVector 的未来发展趋势,探讨其潜在的改进方向和应用前景。

    6.4.1 C++ 标准化

    目前 SmallVector 并非 C++ 标准库的一部分,而是 Folly 库的组件。未来,SmallVector 有可能被 C++ 标准化,成为标准库容器。这将极大地提升 SmallVector 的普及度和应用范围。

    提案与讨论
    C++ 标准委员会一直在关注 SmallVector 这类栈上小对象优化的容器。未来可能会有提案将 SmallVector 或类似的设计纳入 C++ 标准库。

    标准库的演进
    C++ 标准库也在不断演进,例如 C++20 引入了 std::span 等新的工具,为容器的扩展和优化提供了更多可能性。SmallVector 的标准化将是 C++ 标准库容器发展的重要方向之一。

    6.4.2 Capacity 自适应与动态调整

    当前的 SmallVector 的 Capacity 是模板参数,需要在编译时确定。未来的 SmallVector 可能会引入 Capacity 自适应和动态调整的机制,以更好地应对数据规模不确定的场景。

    编译时 Capacity 推导
    可以考虑在某些场景下,编译器能够自动推导 SmallVector 的最佳 Capacity,例如根据初始化列表的大小、函数参数的类型等。

    运行时 Capacity 调整策略
    可以引入更智能的运行时 Capacity 调整策略,例如根据历史数据规模、内存使用情况等,动态地调整 SmallVector 的 Capacity。

    6.4.3 与其他 Folly 组件的深度集成

    SmallVector 作为 Folly 库的一部分,可以与其他 Folly 组件进行更深度的集成,发挥更大的作用。

    与 FBString, FBVector 等协同优化
    SmallVector 可以与 FBString, FBVector 等其他 Folly 容器协同优化,例如在内存分配、移动语义等方面进行更紧密的配合,提升整体性能。

    与 Folly 算法库的集成
    Folly 提供了丰富的算法库,SmallVector 可以与这些算法库进行更紧密的集成,提供更高效的算法实现,例如针对 SmallVector 特性优化的排序、查找等算法。

    6.4.4 硬件加速与 SIMD 优化

    随着硬件技术的不断发展,SIMD (Single Instruction, Multiple Data) 等硬件加速技术在现代处理器中得到广泛应用。未来的 SmallVector 可能会利用硬件加速技术,进一步提升性能。

    SIMD 优化
    可以针对 SmallVector 的常用操作,例如元素拷贝、比较、算术运算等,进行 SIMD 优化,利用 SIMD 指令并行处理多个元素,提高数据处理速度。

    硬件感知分配器
    可以开发硬件感知的分配器,根据硬件特性(例如缓存大小、内存带宽等)优化 SmallVector 的内存分配策略,提高内存访问效率。

    6.4.5 更丰富的 API 和功能

    未来的 SmallVector 可能会增加更丰富的 API 和功能,以满足更广泛的应用需求。

    范围 (Range) 支持
    可以增加对 C++20 范围 (Range) 的支持,提供更方便的范围操作接口,例如范围构造、范围算法等。

    并发 (Concurrency) 支持
    可以考虑在 SmallVector 中引入并发支持,例如线程安全的插入、删除、访问等操作,以满足多线程编程的需求。

    与其他语言的互操作性
    可以考虑增强 SmallVector 与其他语言(例如 Python, Java 等)的互操作性,方便在跨语言的系统中使用 SmallVector。

    总结
    SmallVector 的未来发展前景广阔。随着 C++ 标准的演进、硬件技术的进步以及应用场景的拓展,SmallVector 将会不断完善和优化,在高性能 C++ 开发中发挥越来越重要的作用。开发者应持续关注 SmallVector 的发展动态,学习和掌握其最新特性和最佳实践,以便更好地利用 SmallVector 提升代码性能和效率。

    END_OF_CHAPTER

    7. chapter 7: 总结与展望(总结与展望)

    7.1 SmallVector 的价值与意义回顾(SmallVector 的价值与意义回顾)

    在本书的尾声,我们共同回顾了 Folly 库中 SmallVector 这一强大而精巧的工具。从初识 Folly 库,到深入 SmallVector 的设计哲学、核心概念、高级特性,再到实战应用与 API 全面解析,相信读者已经对 SmallVector 有了系统而深入的理解。现在,让我们再次聚焦 SmallVector 的价值与意义,总结其在现代 C++ 开发中的独特地位。

    性能优势与效率提升SmallVector 最核心的价值在于其在特定场景下卓越的性能表现。相较于 std::vectorSmallVector 通过内联小尺寸数据的栈空间,显著减少了动态内存分配的开销。这在元素数量较小且可预测的场景中,例如:
    ▮▮▮▮ⓑ 频繁创建和销毁的小型容器:避免了堆内存的频繁分配和释放,降低了系统开销。
    ▮▮▮▮ⓒ 对性能敏感的应用:如高性能计算、实时系统、游戏开发等,栈上分配的快速性至关重要。
    ▮▮▮▮ⓓ 缓存友好性:栈内存的连续性有助于提高 CPU 缓存命中率,进一步提升性能。

    内存管理的精细化控制SmallVector 提供了对内存分配策略更精细的控制。通过模板参数 Capacity,开发者可以静态地指定栈上空间的大小,从而在编译期就确定一部分内存布局。这种静态与动态内存管理的结合,使得 SmallVector 既能享受栈内存的快速性,又能应对数据量超出栈空间的动态增长需求。

    代码的简洁性与可读性SmallVector 的 API 设计与 std::vector 高度一致,学习曲线平缓。开发者可以沿用 std::vector 的使用习惯,快速上手 SmallVector,并将其无缝集成到现有的 C++ 项目中。这种熟悉的接口降低了代码维护成本,提升了开发效率。

    广泛的应用场景SmallVector 并非仅仅是一个学术性的优化技巧,而是在工业界得到了广泛的应用。从 Facebook 的 Folly 库将其开源,到众多高性能项目对其青睐,都证明了 SmallVector 在实际工程中的价值。其应用场景涵盖:
    ▮▮▮▮ⓑ 嵌入式系统:资源受限的环境下,栈上分配的优势尤为突出。
    ▮▮▮▮ⓒ 网络编程:高效处理网络数据包,降低延迟。
    ▮▮▮▮ⓓ 游戏开发:优化游戏引擎数据结构,提升帧率。
    ▮▮▮▮ⓔ 高性能计算:加速计算密集型任务。

    现代 C++ 开发的最佳实践SmallVector 体现了现代 C++ 开发中对性能、效率和资源控制的极致追求。它鼓励开发者深入理解内存管理机制,根据具体场景选择最合适的工具,编写出更加高效、健壮的代码。学习和掌握 SmallVector,是提升 C++ 编程技能,迈向高级工程师和专家行列的重要一步。

    总而言之,SmallVector 不仅仅是一个容器,更是一种设计思想的体现,一种性能优化的策略,一种现代 C++ 开发的最佳实践。它以其独特的优势,在众多场景下展现出强大的生命力,值得每一位 C++ 开发者深入学习和掌握。

    7.2 持续学习与深入探索 Folly 库(持续学习与深入探索 Folly 库)

    本书以 SmallVector 为切入点,带领读者领略了 Folly 库的冰山一角。然而,Folly 库作为一个由 Facebook 开源的、经过大规模工业实践检验的 C++ 库,其蕴含的知识和技术远不止于此。持续学习和深入探索 Folly 库,将为你的 C++ 技能提升和职业发展带来无限可能。

    深入探索 Folly 其他组件:Folly 库包含了大量的实用组件,例如:
    FBString:针对字符串操作优化的组件,提供了多种字符串类型和高效的字符串处理算法。
    FBVector:类似于 std::vector,但在某些方面进行了优化,例如内存分配策略。
    ConcurrentSkipListMap:高效的并发跳跃列表映射,适用于高并发场景。
    FuturesPromises:用于异步编程的组件,简化了异步任务的管理和调度。
    IO 框架:提供了高性能的 I/O 抽象层,支持多种 I/O 模型。
    JSON 解析与生成:快速且易于使用的 JSON 处理库。
    Logging 框架:灵活可配置的日志系统。

    深入学习这些组件,可以扩展你的技术栈,提升解决复杂问题的能力。例如,学习 FuturesPromises 可以让你更好地应对并发编程挑战;掌握 IO 框架可以让你构建高性能的网络应用;了解 JSON 处理库可以让你更高效地处理数据交换格式。

    关注 Folly 库的更新与发展:Folly 库是一个活跃的开源项目,不断进行更新和迭代。关注 Folly 库的 GitHub 仓库和社区动态,可以及时了解最新的特性、改进和最佳实践。参与社区讨论,甚至贡献代码,也是深入学习 Folly 库的有效途径。

    结合实际项目应用 Folly 库:理论学习固然重要,但真正的掌握来自于实践。尝试将 Folly 库应用到实际项目中,解决实际问题,才能更深刻地理解其设计思想和使用技巧。可以从小型项目开始,逐步尝试使用 Folly 库的各种组件,积累实战经验。

    拓展 C++ 知识体系:Folly 库的设计和实现,涉及了大量的 C++ 高级特性和编程技巧,例如:模板元编程、移动语义、完美转发、SFINAE(Substitution Failure Is Not An Error, 替换失败不是错误)、RAII(Resource Acquisition Is Initialization, 资源获取即初始化)等。学习 Folly 库的过程,也是一个深入学习和巩固 C++ 知识体系的过程。

    持续学习软件工程最佳实践:Folly 库不仅仅是一个技术库,更蕴含了 Facebook 等大型互联网公司在软件工程方面的最佳实践。学习 Folly 库的设计理念、代码风格、测试方法等,可以提升你的软件工程素养,培养良好的编程习惯。

    学习永无止境,技术日新月异。希望本书能够成为你探索 Folly 库和现代 C++ 开发的起点。在未来的学习和工作中,保持好奇心,不断探索,勇于实践,你将在 C++ 的世界里取得更大的成就!🚀

    END_OF_CHAPTER