C++ STL库详解(容器、迭代器、分配器、实用工具等)

1. 容器(Containers)

顺序容器概述

顺序容器是指按照线性顺序存储元素的容器,允许通过位置(索引)来访问元素。C++ STL提供了多种顺序容器,每种容器在内存管理和操作效率上有不同的特点。

vector

  • 特点

    • 动态数组,支持快速随机访问(O(1)时间复杂度)。
    • 在尾部插入和删除元素效率高(O(1)平均时间复杂度)。
    • 在中间或头部插入/删除元素效率较低(O(n)时间复杂度)。
    • 内存连续分配,支持迭代器随机访问。
  • 适用场景

    • 需要频繁随机访问元素。
    • 主要在尾部进行插入和删除操作。
  • 示例代码

    #include 
    std::vector<int> vec = {1, 2, 3};
    vec.push_back(4); // 尾部插入
    int val = vec[1]; // 随机访问
    

deque

  • 特点

    • 双端队列,支持在头部和尾部高效插入/删除(O(1)时间复杂度)。
    • 支持随机访问(O(1)时间复杂度),但效率略低于vector。
    • 内存分段连续,由多个固定大小的数组块组成。
  • 适用场景

    • 需要在头部和尾部频繁插入/删除。
    • 需要随机访问,但对性能要求不如vector严格。
  • 示例代码

    #include 
    std::deque<int> dq = {1, 2, 3};
    dq.push_front(0); // 头部插入
    dq.push_back(4);  // 尾部插入
    int val = dq[2];  // 随机访问
    

list

  • 特点

    • 双向链表,支持在任意位置高效插入/删除(O(1)时间复杂度)。
    • 不支持随机访问(访问元素需要O(n)时间复杂度)。
    • 内存非连续,通过指针链接节点。
  • 适用场景

    • 需要频繁在任意位置插入/删除元素。
    • 不需要随机访问。
  • 示例代码

    #include 
    std::list<int> lst = {1, 2, 3};
    lst.insert(++lst.begin(), 4); // 在第二个位置插入
    lst.erase(lst.begin());       // 删除头部元素
    

forward_list

  • 特点

    • 单向链表,仅支持单向遍历。
    • 插入/删除效率高(O(1)时间复杂度),但只能从前向后操作。
    • 比list更节省内存(每个节点少一个指针)。
  • 适用场景

    • 只需要单向遍历。
    • 对内存占用敏感。
  • 示例代码

    #include 
    std::forward_list<int> flst = {1, 2, 3};
    flst.insert_after(flst.begin(), 4); // 在第二个位置插入
    flst.erase_after(flst.begin());     // 删除第二个元素
    

array

  • 特点

    • 固定大小的数组,内存连续分配。
    • 支持快速随机访问(O(1)时间复杂度)。
    • 大小在编译时确定,不支持动态扩容。
  • 适用场景

    • 需要固定大小的容器。
    • 对性能要求高,且不需要动态调整大小。
  • 示例代码

    #include 
    std::array<int, 3> arr = {1, 2, 3};
    int val = arr[1]; // 随机访问
    

总结

容器 特点 适用场景
vector 动态数组,尾部操作高效,支持随机访问 需要随机访问或尾部操作
deque 双端队列,头尾操作高效,支持随机访问 需要头尾操作或随机访问
list 双向链表,任意位置操作高效,不支持随机访问 需要频繁插入/删除
forward_list 单向链表,单向遍历,内存占用更少 单向操作且对内存敏感
array 固定大小数组,性能最高,不支持动态调整 固定大小且需高性能

关联容器概述

关联容器是C++标准模板库(STL)中的一类容器,它们通过键(key)来存储和访问元素。关联容器中的元素是按照特定的排序准则自动排序的,这使得查找操作非常高效。

关联容器主要包括以下四种类型:

  1. set
  2. multiset
  3. map
  4. multimap

set

set是一个有序的关联容器,它包含唯一键(key)的元素。每个元素在set中只能出现一次。

特点:

  • 元素自动排序(默认升序)
  • 不允许重复元素
  • 基于红黑树实现
  • 查找、插入、删除操作的时间复杂度为O(log n)

常用操作:

#include 
std::set<int> s;
s.insert(10);    // 插入元素
s.erase(10);     // 删除元素
s.find(10);      // 查找元素
s.size();        // 返回元素数量
s.empty();       // 判断是否为空

multiset

multiset类似于set,但允许存储多个值相同的元素。

特点:

  • 元素自动排序
  • 允许重复元素
  • 基于红黑树实现
  • 查找、插入、删除操作的时间复杂度为O(log n)

常用操作:

#include 
std::multiset<int> ms;
ms.insert(10);    // 可以插入多个10
ms.count(10);     // 返回值为10的元素个数

map

map是一个关联数组,存储键值对(key-value pairs),其中键是唯一的。

特点:

  • 按键自动排序
  • 每个键只能对应一个值
  • 基于红黑树实现
  • 查找、插入、删除操作的时间复杂度为O(log n)

常用操作:

#include 
std::map<std::string, int> m;
m["apple"] = 5;    // 插入或修改元素
m.erase("apple");  // 删除元素
m.find("apple");   // 查找元素
m.size();          // 返回元素数量

multimap

multimap类似于map,但允许一个键对应多个值。

特点:

  • 按键自动排序
  • 允许一个键对应多个值
  • 基于红黑树实现
  • 查找、插入、删除操作的时间复杂度为O(log n)

常用操作:

#include 
std::multimap<std::string, int> mm;
mm.insert({"apple", 5});    // 可以插入多个"apple"键
mm.count("apple");         // 返回键为"apple"的元素个数

比较总结

特性 set multiset map multimap
键唯一性
存储内容 键值对 键值对
排序 按键 按键 按键 按键
允许重复

这些关联容器都定义在头文件中,使用时需要包含相应的头文件。


无序容器概述

无序容器是C++标准模板库(STL)中基于哈希表实现的容器,它们不保证元素的顺序,但提供了快速的查找、插入和删除操作。无序容器包括以下四种类型:

  1. unordered_set
  2. unordered_multiset
  3. unordered_map
  4. unordered_multimap

unordered_set

  • 特点

    • 存储唯一键值的集合
    • 不允许重复元素
    • 元素顺序不确定
    • 基于哈希表实现
  • 常用操作

    • insert():插入元素
    • erase():删除元素
    • find():查找元素
    • count():统计元素出现次数(0或1)
  • 示例

    std::unordered_set<int> us = {1, 2, 3, 4, 5};
    us.insert(6);
    us.erase(3);
    

unordered_multiset

  • 特点

    • 允许存储重复键值
    • 其他特性与unordered_set类似
  • 常用操作

    • unordered_set基本相同
    • count()可能返回大于1的值
  • 示例

    std::unordered_multiset<int> ums = {1, 2, 2, 3, 3, 3};
    ums.insert(2);
    

unordered_map

  • 特点

    • 存储键值对(key-value)
    • 键唯一,不允许重复
    • 基于哈希表实现
    • 快速查找(平均O(1))
  • 常用操作

    • insert()[]:插入元素
    • erase():删除元素
    • find():查找元素
    • at():访问元素
  • 示例

    std::unordered_map<std::string, int> um = {{"apple", 1}, {"banana", 2}};
    um["orange"] = 3;
    

unordered_multimap

  • 特点

    • 允许键重复
    • 其他特性与unordered_map类似
  • 常用操作

    • unordered_map基本相同
    • count()可能返回大于1的值
    • equal_range():获取指定键的所有元素
  • 示例

    std::unordered_multimap<std::string, int> umm = {{"apple", 1}, {"apple", 2}};
    umm.insert({"banana", 3});
    

共同特性

  1. 哈希函数:所有无序容器都使用哈希函数来确定元素的存储位置
  2. 负载因子:可以通过load_factor()max_load_factor()管理
  3. 桶接口:提供bucket_count(), bucket_size()等桶相关操作
  4. 迭代器:提供前向迭代器,但不保证迭代顺序

性能特点

  • 平均情况下,插入、删除和查找操作的时间复杂度为O(1)
  • 最坏情况下可能退化到O(n)
  • 比有序容器(如set/map)更快,但不保持元素顺序

适用场景

  • 需要快速查找而不关心元素顺序
  • 不需要保持元素插入顺序或排序顺序
  • 元素不需要频繁遍历

容器适配器概述

容器适配器是STL中对基础容器进行封装的一类特殊容器,它们基于现有的序列容器(如vectordequelist)实现特定的数据结构接口。与普通容器不同,适配器限制了部分功能,仅提供符合其设计目的的接口。STL中主要包含以下三种容器适配器:

  1. stack

    • 功能:实现后进先出(LIFO)的栈结构。
    • 底层容器:默认使用deque,也可指定vectorlist
    • 核心操作
      • push():向栈顶插入元素。
      • pop():移除栈顶元素(无返回值)。
      • top():访问栈顶元素(不删除)。
      • empty()size():检查状态。
  2. queue

    • 功能:实现先进先出(FIFO)的队列结构。
    • 底层容器:默认使用deque,也可指定list(不可用vector,因需支持前端删除)。
    • 核心操作
      • push():向队尾插入元素。
      • pop():移除队首元素(无返回值)。
      • front()back():分别访问队首和队尾元素。
  3. priority_queue

    • 功能:实现优先级队列(堆结构),默认最大元素优先。
    • 底层容器:默认使用vector,也可指定deque
    • 核心操作
      • push():插入元素并自动排序。
      • pop():删除优先级最高的元素。
      • top():访问堆顶元素(即最高优先级元素)。
    • 自定义排序:可通过比较函数(如greater)修改优先级规则。

共同特性

  • 不支持迭代器:适配器强调特定操作,禁止直接遍历元素。
  • 依赖底层容器:性能受底层容器影响(如stackvector可能触发频繁扩容)。
  • 接口简化:仅暴露与数据结构相关的操作(如stackback())。

示例代码片段

// stack 示例
std::stack<int> s;
s.push(10); 
s.pop();     

// queue 示例
std::queue<int> q;
q.push(20);  
q.pop();     

// priority_queue 示例
std::priority_queue<int, std::vector<int>, std::greater<int>> pq;
pq.push(5);  // 最小元素优先

2. 迭代器(Iterators)

输入迭代器

输入迭代器(Input Iterator)是 C++ STL 中最简单的迭代器类型之一,主要用于从容器中读取数据。它支持单向遍历,即只能向前移动(通过 ++ 操作符),并且只能读取元素的值(通过 * 操作符)。

主要特点:
  1. 只读访问:只能读取元素的值,不能修改元素的值。
  2. 单向移动:只能通过 ++ 操作符向前移动,不能后退或随机访问。
  3. 一次性遍历:输入迭代器通常只能遍历一次,遍历完成后迭代器可能会失效。
支持的操作:
  • *iter:解引用迭代器,获取当前元素的值。
  • iter->member:访问当前元素的成员(如果元素是对象)。
  • ++iteriter++:将迭代器移动到下一个元素。
  • iter1 == iter2iter1 != iter2:比较两个迭代器是否指向同一个位置。
典型应用场景:

输入迭代器通常用于从输入流(如 std::istream)或某些算法中读取数据。例如,std::istream_iterator 就是一个输入迭代器。

示例代码:
#include 
#include 
#include 

int main() {
    std::vector<int> vec = {1, 2, 3, 4, 5};
    // 使用输入迭代器遍历 vector
    for (auto it = vec.begin(); it != vec.end(); ++it) {
        std::cout << *it << " "; // 读取元素的值
    }
    return 0;
}
注意事项:
  • 输入迭代器不支持多次遍历同一序列。
  • 不能保证两个输入迭代器之间的比较是有效的(例如,从输入流中读取时)。

输出迭代器

输出迭代器(Output Iterator)是 C++ STL 中的一种迭代器类型,主要用于向容器中写入数据。它支持单向遍历,并且只能用于写入操作,不能用于读取数据。

特点
  1. 只写不读:输出迭代器只能用于写入数据,不能用于读取数据。
  2. 单向移动:只能向前移动(++操作),不能后退或随机访问。
  3. 单次遍历:通常只能遍历一次,不支持多次遍历同一范围。
支持的操作
  • 解引用(*iter:返回一个左值引用,用于写入数据。
  • 递增(++iteriter++:移动到下一个位置。
  • 赋值(*iter = value:将值写入迭代器指向的位置。
典型应用场景
  • 与算法结合使用,如 std::copystd::transform 等,用于将数据写入容器。
  • 输出到流(如 std::ostream_iterator)。
示例代码
#include 
#include 
#include 

int main() {
    std::vector<int> vec(5); // 初始化大小为5的vector
    auto it = vec.begin();   // 获取输出迭代器(实际是随机访问迭代器,但支持输出操作)

    // 使用输出迭代器写入数据
    *it = 10;    // 写入第一个元素
    ++it;        // 移动到下一个位置
    *it = 20;    // 写入第二个元素

    // 使用算法写入数据(std::fill 使用输出迭代器)
    std::fill(vec.begin(), vec.end(), 42);

    return 0;
}
注意事项
  • 输出迭代器不保证写入后数据是否被立即存储(如流迭代器可能延迟写入)。
  • 通常不检查边界,需确保迭代器有效且容器有足够空间。

前向迭代器

前向迭代器(Forward Iterator)是 C++ STL 中的一种迭代器类型,它提供了单向遍历容器元素的能力。它比输入迭代器(Input Iterator)和输出迭代器(Output Iterator)功能更强,但比双向迭代器(Bidirectional Iterator)和随机访问迭代器(Random Access Iterator)功能更弱。

特点
  1. 单向移动:前向迭代器只能向前移动(通过 ++ 运算符),不能向后移动。
  2. 可读可写:支持读取和修改元素的值(如果容器允许)。
  3. 可多次遍历:可以多次遍历同一个序列,不会失效(除非容器被修改)。
  4. 支持默认构造和拷贝:可以默认构造和拷贝构造。
支持的操作
  • 解引用*iter 获取当前元素的值。
  • 成员访问iter->member 访问当前元素的成员。
  • 递增++iteriter++ 移动到下一个元素。
  • 比较iter1 == iter2iter1 != iter2 判断两个迭代器是否指向同一位置。
适用场景

前向迭代器适用于单向遍历的容器,如 std::forward_list 和某些哈希表的实现(如 std::unordered_setstd::unordered_map)。

示例代码
#include 
#include 

int main() {
    std::forward_list<int> flist = {1, 2, 3, 4, 5};
    
    // 使用前向迭代器遍历
    for (auto it = flist.begin(); it != flist.end(); ++it) {
        std::cout << *it << " ";
    }
    std::cout << std::endl;
    
    return 0;
}
注意事项
  • 前向迭代器不支持 -- 操作,因此不能反向遍历。
  • 某些算法(如 std::reverse)需要双向迭代器或随机访问迭代器,不能直接用于前向迭代器。

双向迭代器

双向迭代器(Bidirectional Iterator)是C++标准模板库(STL)中的一种迭代器类别,它扩展了前向迭代器的功能,允许在序列中向前和向后移动。

特性
  1. 双向移动:可以递增(++)和递减(--)操作。
  2. 多遍扫描:可以多次遍历同一序列。
  3. 读写能力:可以解引用(*)以读取或修改元素的值。
支持的操作
  • 递增++):移动到下一个元素。
  • 递减--):移动到上一个元素。
  • 解引用*):访问当前元素的值。
  • 成员访问->):访问当前元素的成员(如果元素是对象或结构体)。
  • 比较==!=):判断两个迭代器是否指向同一位置。
示例
#include 
#include 

int main() {
    std::list<int> lst = {1, 2, 3, 4, 5};
    std::list<int>::iterator it = lst.begin();

    // 向前移动
    ++it;
    std::cout << *it << std::endl; // 输出: 2

    // 向后移动
    --it;
    std::cout << *it << std::endl; // 输出: 1

    return 0;
}
适用容器

双向迭代器通常用于以下STL容器:

  • std::list
  • std::set
  • std::multiset
  • std::map
  • std::multimap
注意事项
  1. 随机访问不支持:双向迭代器不支持随机访问(如it + nit[n]),这是随机访问迭代器的特性。
  2. 性能:双向迭代器的递增和递减操作通常是常数时间复杂度(O(1))。

随机访问迭代器

随机访问迭代器(Random Access Iterator)是 C++ STL 中最强大的迭代器类型之一,它提供了最丰富的操作功能,允许以任意顺序访问容器中的元素。

特性
  1. 双向移动:可以向前(++)和向后(--)移动。
  2. 随机访问:支持通过 +- 运算符直接跳转到任意位置(如 iter + 5)。
  3. 下标访问:支持类似数组的下标操作(如 iter[n])。
  4. 比较操作:支持 ==!=<><=>= 等比较运算符。
支持的容器
  • std::vector
  • std::deque
  • std::array
  • 普通数组(指针也是随机访问迭代器)
常用操作示例
std::vector<int> vec = {1, 2, 3, 4, 5};
auto it = vec.begin();

// 随机访问
it = it + 3; // 现在指向第4个元素(4)
int val = it[1]; // 获取下一个元素(5)

// 比较
if (it > vec.begin()) {
    // 迭代器位置比较
}
与其他迭代器的区别
  • 输入/输出迭代器:只能单向移动,且只能读/写一次。
  • 前向迭代器:可单向多次读写,但不能随机访问。
  • 双向迭代器:可双向移动,但仍不支持随机跳转。

随机访问迭代器是 STL 算法高效实现的关键,如 std::sort() 就需要这种迭代器。


连续迭代器 (Contiguous Iterator)

连续迭代器是 C++ STL 中迭代器的一种类型,属于随机访问迭代器的一个特例。它满足随机访问迭代器的所有要求,并额外保证指向的元素在内存中是连续存储的。

特性
  1. 内存连续性:连续迭代器指向的元素在内存中是连续存储的,类似于数组的存储方式。
  2. 指针兼容性:可以通过 &*it 直接获取元素的指针,且指针算术有效。
  3. 高效访问:支持常数时间的随机访问,类似于指针。
满足连续迭代器的容器
  • std::vector
  • std::array
  • std::string(C++17 起)
  • 原生数组(如 int arr[10]
操作示例
std::vector<int> vec = {1, 2, 3, 4, 5};
auto it = vec.begin(); // 连续迭代器

// 随机访问
int third = it[2]; // 3

// 指针算术
int* ptr = &*it; // 获取第一个元素的指针
int next = *(ptr + 1); // 2
注意事项
  • 连续迭代器是 C++17 引入的概念,之前的标准未明确区分。
  • 只有满足连续存储的容器才支持连续迭代器,例如 std::deque 虽然是随机访问迭代器,但不是连续迭代器。

连续迭代器的高效性和指针兼容性使其在需要直接内存操作或与 C 接口交互时非常有用。


3. 算法(Algorithms)

非修改算法

非修改算法是指那些不会改变容器中元素的值或顺序的算法。它们主要用于查找、计数或遍历容器中的元素。以下是几个常见的非修改算法:

find
template <class InputIterator, class T>
InputIterator find(InputIterator first, InputIterator last, const T& value);
  • 功能:在范围 [first, last) 中查找第一个等于 value 的元素。
  • 返回值:如果找到,返回指向该元素的迭代器;否则返回 last
  • 示例
    std::vector<int> v = {1, 2, 3, 4, 5};
    auto it = std::find(v.begin(), v.end(), 3); // 返回指向 3 的迭代器
    
count
template <class InputIterator, class T>
typename iterator_traits<InputIterator>::difference_type
count(InputIterator first, InputIterator last, const T& value);
  • 功能:统计范围 [first, last) 中等于 value 的元素的个数。
  • 返回值:返回匹配的元素数量。
  • 示例
    std::vector<int> v = {1, 2, 2, 3, 2};
    int cnt = std::count(v.begin(), v.end(), 2); // 返回 3
    
equal
template <class InputIterator1, class InputIterator2>
bool equal(InputIterator1 first1, InputIterator1 last1, InputIterator2 first2);
  • 功能:比较两个范围 [first1, last1)[first2, first2 + (last1 - first1)) 的元素是否相等。
  • 返回值:如果所有对应元素都相等,返回 true;否则返回 false
  • 示例
    std::vector<int> v1 = {1, 2, 3};
    std::vector<int> v2 = {1, 2, 3};
    bool isEqual = std::equal(v1.begin(), v1.end(), v2.begin()); // 返回 true
    
for_each
template <class InputIterator, class Function>
Function for_each(InputIterator first, InputIterator last, Function f);
  • 功能:对范围 [first, last) 中的每个元素应用函数 f
  • 返回值:返回函数对象 f(可以是可调用对象或函数指针)。
  • 示例
    std::vector<int> v = {1, 2, 3};
    std::for_each(v.begin(), v.end(), [](int x) { std::cout << x << " "; }); // 输出 1 2 3
    

这些算法都不会修改容器中的元素,适合用于只读操作。


修改算法:copy、transform、fill、swap

copy

copy 用于将一个范围内的元素复制到另一个位置。
语法

OutputIt copy(InputIt first, InputIt last, OutputIt d_first);
  • firstlast 是源范围的迭代器。
  • d_first 是目标范围的起始迭代器。
  • 返回目标范围的结束迭代器(d_first + (last - first))。

示例

std::vector<int> src = {1, 2, 3, 4, 5};
std::vector<int> dest(5);
std::copy(src.begin(), src.end(), dest.begin());
// dest 变为 {1, 2, 3, 4, 5}

transform

transform 对范围内的每个元素应用一个操作,并将结果存储到另一个范围。
语法

OutputIt transform(InputIt first1, InputIt last1, OutputIt d_first, UnaryOp op);
OutputIt transform(InputIt1 first1, InputIt1 last1, InputIt2 first2, OutputIt d_first, BinaryOp op);
  1. 一元操作:对 [first1, last1) 的每个元素应用 op,结果存到 d_first
  2. 二元操作:对 [first1, last1)first2 开始的对应元素应用 op,结果存到 d_first

示例

std::vector<int> v = {1, 2, 3};
std::vector<int> result(3);
// 一元操作:平方
std::transform(v.begin(), v.end(), result.begin(), [](int x) { return x * x; });
// result 变为 {1, 4, 9}

fill

fill 将一个范围内的所有元素赋为指定值。
语法

void fill(ForwardIt first, ForwardIt last, const T& value);
  • firstlast 是目标范围的迭代器。
  • value 是要赋的值。

示例

std::vector<int> v(5);
std::fill(v.begin(), v.end(), 42);
// v 变为 {42, 42, 42, 42, 42}

swap

swap 交换两个对象的值。
语法

void swap(T& a, T& b);
  • ab 是要交换的对象。
  • 适用于基本类型、容器、智能指针等。

示例

int x = 10, y = 20;
std::swap(x, y);
// x = 20, y = 10

注意

  • 对于容器(如 std::vector),swap 是高效的(仅交换内部指针)。

排序算法:sort、stable_sort、partial_sort

sort
  • 功能:对指定范围内的元素进行排序,默认按升序排列。
  • 特点
    • 不保证相等元素的相对顺序(不稳定排序)。
    • 平均时间复杂度为 O(N log N)。
  • 用法
    #include 
    #include 
    
    std::vector<int> v = {4, 2, 5, 3, 1};
    std::sort(v.begin(), v.end()); // 升序
    std::sort(v.begin(), v.end(), std::greater<int>()); // 降序
    
stable_sort
  • 功能:对指定范围内的元素进行排序,并保持相等元素的相对顺序(稳定排序)。
  • 特点
    • 时间复杂度为 O(N log N) 或 O(N (log N)^2),取决于实现。
    • 适用于需要保持相等元素原始顺序的场景。
  • 用法
    #include 
    #include 
    
    std::vector<int> v = {4, 2, 5, 3, 1, 2};
    std::stable_sort(v.begin(), v.end()); // 保持两个2的相对顺序
    
partial_sort
  • 功能:对指定范围内的部分元素进行排序,确保前 N 个元素是有序的,其余元素顺序未指定。
  • 特点
    • 时间复杂度为 O(N log K),其中 K 是部分排序的元素数量。
    • 适用于只需要前 K 个最小或最大元素的场景。
  • 用法
    #include 
    #include 
    
    std::vector<int> v = {4, 2, 5, 3, 1};
    // 部分排序前3个元素
    std::partial_sort(v.begin(), v.begin() + 3, v.end());
    // 此时前3个元素为1, 2, 3,其余未排序
    

lower_bound

lower_bound 是 C++ STL 中的一个二分查找算法,用于在已排序的范围内查找第一个不小于给定值的元素的位置。

函数原型
template <class ForwardIterator, class T>
ForwardIterator lower_bound(ForwardIterator first, ForwardIterator last, const T& value);
参数说明
  • firstlast:定义查找范围的迭代器,范围为 [first, last)
  • value:要查找的目标值。
返回值
  • 返回指向第一个不小于 value 的元素的迭代器。
  • 如果所有元素都小于 value,则返回 last
示例
#include 
#include 
#include 

int main() {
    std::vector<int> v = {1, 2, 4, 4, 5, 6};
    auto it = std::lower_bound(v.begin(), v.end(), 4);
    if (it != v.end()) {
        std::cout << "First element not less than 4 is at position " << (it - v.begin()) << std::endl;
    }
    return 0;
}

输出:

First element not less than 4 is at position 2

upper_bound

upper_bound 是 C++ STL 中的另一个二分查找算法,用于在已排序的范围内查找第一个大于给定值的元素的位置。

函数原型
template <class ForwardIterator, class T>
ForwardIterator upper_bound(ForwardIterator first, ForwardIterator last, const T& value);
参数说明
  • firstlast:定义查找范围的迭代器,范围为 [first, last)
  • value:要查找的目标值。
返回值
  • 返回指向第一个大于 value 的元素的迭代器。
  • 如果所有元素都不大于 value,则返回 last
示例
#include 
#include 
#include 

int main() {
    std::vector<int> v = {1, 2, 4, 4, 5, 6};
    auto it = std::upper_bound(v.begin(), v.end(), 4);
    if (it != v.end()) {
        std::cout << "First element greater than 4 is at position " << (it - v.begin()) << std::endl;
    }
    return 0;
}

输出:

First element greater than 4 is at position 4

关键区别

  • lower_bound 返回的是第一个不小于 value 的元素。
  • upper_bound 返回的是第一个大于 value 的元素。
结合使用

这两个函数通常一起使用来查找某个值在有序范围内的所有出现位置。例如:

auto lower = std::lower_bound(v.begin(), v.end(), 4);
auto upper = std::upper_bound(v.begin(), v.end(), 4);
// 范围 [lower, upper) 包含所有等于 4 的元素

merge

merge 是 C++ STL 中的一个集合算法,用于将两个已排序的序列合并为一个新的有序序列。它不会修改原始的两个输入序列,而是将结果输出到目标容器中。

函数原型

template <class InputIterator1, class InputIterator2, class OutputIterator>
OutputIterator merge(InputIterator1 first1, InputIterator1 last1,
                    InputIterator2 first2, InputIterator2 last2,
                    OutputIterator result);

参数说明

  • first1, last1:第一个输入序列的起始和结束迭代器。
  • first2, last2:第二个输入序列的起始和结束迭代器。
  • result:目标容器的起始迭代器,用于存储合并后的结果。

示例

#include 
#include 
#include 

int main() {
    std::vector<int> v1 = {1, 3, 5};
    std::vector<int> v2 = {2, 4, 6};
    std::vector<int> result(v1.size() + v2.size());

    std::merge(v1.begin(), v1.end(), v2.begin(), v2.end(), result.begin());

    for (int num : result) {
        std::cout << num << " ";
    }
    // 输出:1 2 3 4 5 6
    return 0;
}

set_union

set_union 是 C++ STL 中的一个集合算法,用于计算两个有序序列的并集,并将结果输出到目标容器中。结果序列也是有序的,并且不包含重复元素。

函数原型

template <class InputIterator1, class InputIterator2, class OutputIterator>
OutputIterator set_union(InputIterator1 first1, InputIterator1 last1,
                         InputIterator2 first2, InputIterator2 last2,
                         OutputIterator result);

参数说明

  • first1, last1:第一个输入序列的起始和结束迭代器。
  • first2, last2:第二个输入序列的起始和结束迭代器。
  • result:目标容器的起始迭代器,用于存储并集结果。

示例

#include 
#include 
#include 

int main() {
    std::vector<int> v1 = {1, 2, 3};
    std::vector<int> v2 = {2, 3, 4};
    std::vector<int> result(v1.size() + v2.size());

    auto it = std::set_union(v1.begin(), v1.end(), v2.begin(), v2.end(), result.begin());
    result.resize(it - result.begin());

    for (int num : result) {
        std::cout << num << " ";
    }
    // 输出:1 2 3 4
    return 0;
}

includes

includes 是 C++ STL 中的一个集合算法,用于检查一个有序序列是否包含另一个有序序列的所有元素(即是否为子集)。

函数原型

template <class InputIterator1, class InputIterator2>
bool includes(InputIterator1 first1, InputIterator1 last1,
              InputIterator2 first2, InputIterator2 last2);

参数说明

  • first1, last1:第一个输入序列的起始和结束迭代器(可能是更大的集合)。
  • first2, last2:第二个输入序列的起始和结束迭代器(可能是子集)。

返回值

  • 如果第二个序列的所有元素都包含在第一个序列中,返回 true,否则返回 false

示例

#include 
#include 
#include 

int main() {
    std::vector<int> v1 = {1, 2, 3, 4, 5};
    std::vector<int> v2 = {2, 3, 4};

    bool result = std::includes(v1.begin(), v1.end(), v2.begin(), v2.end());
    std::cout << (result ? "包含" : "不包含"); // 输出:包含
    return 0;
}

4. 函数对象(Function Objects)

算术函数:plus、minus、multiplies

1. plus
  • 功能:实现两个数的加法运算。
  • 定义
    template <class T> struct plus;
    
  • 使用方式
    #include 
    std::plus<int> add;
    int result = add(3, 4); // 结果为7
    
  • 特点
    • 默认模板参数为 T,通常用于基本数据类型(如 intfloat)。
    • 也可以用于自定义类型,前提是重载了 operator+
2. minus
  • 功能:实现两个数的减法运算。
  • 定义
    template <class T> struct minus;
    
  • 使用方式
    #include 
    std::minus<int> subtract;
    int result = subtract(5, 3); // 结果为2
    
  • 特点
    • 默认模板参数为 T,适用于支持 operator- 的类型。
    • 常用于数值计算或算法中(如 std::transform)。
3. multiplies
  • 功能:实现两个数的乘法运算。
  • 定义
    template <class T> struct multiplies;
    
  • 使用方式
    #include 
    std::multiplies<int> multiply;
    int result = multiply(2, 3); // 结果为6
    
  • 特点
    • 默认模板参数为 T,要求类型支持 operator*
    • 可用于数值计算或自定义类型的乘法操作。
共同点
  • 均定义在 头文件中。
  • 是函数对象(仿函数),可通过 operator() 调用。
  • 通常与STL算法(如 std::accumulatestd::transform)配合使用。
示例代码
#include 
#include 
#include 
#include 

int main() {
    std::vector<int> v = {1, 2, 3, 4, 5};
    
    // 使用 plus 计算总和
    int sum = std::accumulate(v.begin(), v.end(), 0, std::plus<int>());
    std::cout << "Sum: " << sum << std::endl; // 输出15
    
    // 使用 multiplies 计算乘积
    int product = std::accumulate(v.begin(), v.end(), 1, std::multiplies<int>());
    std::cout << "Product: " << product << std::endl; // 输出120
    
    return 0;
}

比较函数:less、greater、equal_to

在 C++ STL 中,lessgreaterequal_to 是预定义的函数对象(也称为仿函数),用于比较两个值。它们通常用于算法、容器(如 setmappriority_queue)或排序函数(如 sort)中,以定义元素的比较逻辑。

1. less
  • 功能:比较两个值是否满足 a < b
  • 定义std::less 是一个模板类,其中 T 是可比较的类型(如 intdoublestring 等)。
  • 用法
    #include 
    std::less<int> compare;
    bool result = compare(3, 5); // 返回 true,因为 3 < 5
    
  • 常见用途
    • 默认情况下,setmap 使用 less 对元素进行升序排序。
    • 可以显式传递给 sort 函数:
      std::vector<int> v = {5, 2, 8, 1};
      std::sort(v.begin(), v.end(), std::less<int>()); // 升序排序
      
2. greater
  • 功能:比较两个值是否满足 a > b
  • 定义std::greaterless 的逆操作。
  • 用法
    #include 
    std::greater<int> compare;
    bool result = compare(5, 3); // 返回 true,因为 5 > 3
    
  • 常见用途
    • 用于降序排序:
      std::vector<int> v = {5, 2, 8, 1};
      std::sort(v.begin(), v.end(), std::greater<int>()); // 降序排序
      
    • 可以用于 priority_queue 实现最大堆:
      std::priority_queue<int, std::vector<int>, std::greater<int>> pq;
      
3. equal_to
  • 功能:比较两个值是否满足 a == b
  • 定义std::equal_to 用于判断两个值是否相等。
  • 用法
    #include 
    std::equal_to<int> compare;
    bool result = compare(3, 3); // 返回 true
    
  • 常见用途
    • 用于需要判断相等性的算法中,如 find_if 或自定义比较逻辑。
    • 可以结合其他函数对象使用,例如在哈希容器中。
总结
  • lessgreaterequal_to 是 STL 提供的标准比较函数对象。
  • 它们通常用于排序、容器和算法中,以定义元素的比较规则。
  • 通过模板参数 T,可以适配不同类型的比较需求。

逻辑函数:logical_and、logical_or

概述

logical_andlogical_or 是 C++ STL 提供的两个逻辑函数对象(function objects),用于执行逻辑与(AND)和逻辑或(OR)操作。它们通常用于算法中,作为谓词(predicate)或比较函数。

头文件

这两个函数对象定义在 头文件中。

使用方法
  1. logical_and

    • 用于执行逻辑与操作,即 a && b
    • 语法:
      std::logical_and<bool> func;
      bool result = func(a, b);
      
    • 示例:
      #include 
      #include 
      
      int main() {
          std::logical_and<bool> and_op;
          bool a = true;
          bool b = false;
          std::cout << std::boolalpha << and_op(a, b) << std::endl; // 输出 false
          return 0;
      }
      
  2. logical_or

    • 用于执行逻辑或操作,即 a || b
    • 语法:
      std::logical_or<bool> func;
      bool result = func(a, b);
      
    • 示例:
      #include 
      #include 
      
      int main() {
          std::logical_or<bool> or_op;
          bool a = true;
          bool b = false;
          std::cout << std::boolalpha << or_op(a, b) << std::endl; // 输出 true
          return 0;
      }
      
应用场景
  • 通常与 STL 算法(如 std::transformstd::accumulate)结合使用,对容器中的元素进行逻辑运算。
  • 示例(结合 std::transform):
    #include 
    #include 
    #include 
    #include 
    
    int main() {
        std::vector<bool> v1 = {true, false, true};
        std::vector<bool> v2 = {false, false, true};
        std::vector<bool> result(v1.size());
    
        std::transform(v1.begin(), v1.end(), v2.begin(), result.begin(), std::logical_and<bool>());
    
        for (bool val : result) {
            std::cout << std::boolalpha << val << " "; // 输出 false false true
        }
        return 0;
    }
    
注意事项
  • logical_andlogical_or 是模板类,通常用于布尔类型(bool),但也可以用于其他可转换为布尔值的类型。
  • 它们是函数对象,可以直接调用 operator(),也可以作为参数传递给其他函数或算法。

函数适配器:bind、not1、mem_fn

1. std::bind

std::bind 是一个通用的函数适配器,用于将函数或成员函数绑定到特定的参数或对象上,生成一个新的可调用对象。它允许部分应用参数(即固定某些参数的值),或者重新排列参数的顺序。

  • 基本用法

    #include 
    #include 
    
    void print_sum(int a, int b) {
        std::cout << a + b << std::endl;
    }
    
    int main() {
        auto bound_func = std::bind(print_sum, 10, std::placeholders::_1);
        bound_func(20); // 输出 30(10 + 20)
        return 0;
    }
    
    • std::placeholders::_1 是一个占位符,表示调用 bound_func 时传入的第一个参数。
  • 绑定成员函数

    struct MyClass {
        void print(int x) { std::cout << x << std::endl; }
    };
    
    int main() {
        MyClass obj;
        auto bound_member = std::bind(&MyClass::print, &obj, std::placeholders::_1);
        bound_member(42); // 输出 42
        return 0;
    }
    
    • 绑定成员函数时,需要传递对象的指针(&obj)作为第一个参数。
2. std::not1

std::not1 是一个一元函数适配器,用于对一个一元谓词(返回 bool 的函数对象)取反。

  • 基本用法

    #include 
    #include 
    #include 
    
    struct IsOdd {
        bool operator()(int x) const { return x % 2 != 0; }
    };
    
    int main() {
        std::vector<int> v = {1, 2, 3, 4, 5};
        auto is_even = std::not1(IsOdd());
        auto it = std::find_if(v.begin(), v.end(), is_even);
        if (it != v.end()) {
            std::cout << "First even number: " << *it << std::endl; // 输出 2
        }
        return 0;
    }
    
    • std::not1IsOdd 的返回值取反,得到一个新的谓词 is_even
  • 限制

    • 只能用于一元谓词(即接受一个参数的函数对象)。
    • 需要函数对象定义 argument_type 类型(如 IsOdd 中的 operator() 参数类型)。
3. std::mem_fn

std::mem_fn 是一个函数适配器,用于将成员函数转换为可调用对象,可以绑定到对象或对象指针上。

  • 基本用法

    #include 
    #include 
    #include 
    
    struct Person {
        std::string name;
        void print() const { std::cout << name << std::endl; }
    };
    
    int main() {
        std::vector<Person> people = {{"Alice"}, {"Bob"}, {"Charlie"}};
        std::for_each(people.begin(), people.end(), std::mem_fn(&Person::print));
        // 输出:
        // Alice
        // Bob
        // Charlie
        return 0;
    }
    
    • std::mem_fn(&Person::print) 将成员函数 print 转换为可调用对象,可以直接用于算法中。
  • 支持多态

    struct Base {
        virtual void foo() { std::cout << "Base" << std::endl; }
    };
    
    struct Derived : Base {
        void foo() override { std::cout << "Derived" << std::endl; }
    };
    
    int main() {
        Derived d;
        Base* ptr = &d;
        auto call_foo = std::mem_fn(&Base::foo);
        call_foo(ptr); // 输出 "Derived"(多态调用)
        return 0;
    }
    
    • std::mem_fn 支持虚函数的动态绑定。
总结
  • std::bind:通用函数适配器,支持参数绑定和重排。
  • std::not1:对一元谓词取反。
  • std::mem_fn:将成员函数转换为可调用对象。

5. 分配器(Allocators)

标准分配器(std::allocator)

基本概念

标准分配器是C++标准库中用于内存管理的模板类,定义在头文件中。它封装了内存分配和释放的操作,是STL容器默认使用的内存分配方式。其核心作用是:

  • 提供类型化的内存分配/释放接口
  • 将内存分配与对象构造分离
  • 支持STL容器的内存管理需求
主要成员函数
T* allocate(size_t n);  // 分配未初始化的内存
void deallocate(T* p, size_t n);  // 释放已分配的内存

template<typename U, typename... Args>
void construct(U* p, Args&&... args);  // 在指定位置构造对象(C++17后弃用)

void destroy(T* p);  // 销毁对象但不释放内存(C++17后弃用)
使用示例
std::allocator<int> alloc;
int* p = alloc.allocate(5);  // 分配5个int的空间

// 构造对象
for(int i=0; i<5; ++i) 
    alloc.construct(p+i, i*10); 

// 使用内存...
for(int i=0; i<5; ++i)
    std::cout << p[i] << " ";

// 销毁对象
for(int i=0; i<5; ++i)
    alloc.destroy(p+i);

alloc.deallocate(p, 5);  // 释放内存
特点
  1. 类型安全:分配器是模板类,与特定类型绑定
  2. 可替换性:容器允许通过模板参数指定自定义分配器
  3. 效率优化:标准实现通常直接调用::operator new/delete
注意事项
  • C++17后construct()destroy()被弃用,建议使用std::allocator_traits或placement new
  • 自定义分配器需要满足分配器完整性要求(Allocator completeness requirements)
  • 在多线程环境中需要保证线程安全

自定义分配器

自定义分配器(Custom Allocator)是 C++ STL 中的一个重要概念,允许用户控制容器(如 std::vectorstd::list 等)的内存分配行为。默认情况下,STL 容器使用 std::allocator 作为内存分配器,但用户可以通过自定义分配器来优化内存管理,例如实现内存池、共享内存或其他特殊需求。

基本结构

自定义分配器需要满足以下要求:

  1. 类型定义:必须提供 value_typepointerconst_pointer 等类型别名。
  2. 分配与释放:实现 allocate()deallocate() 方法,分别用于分配和释放内存。
  3. 构造与析构:实现 construct()destroy() 方法(C++11 后通常使用 std::allocator_traits 自动生成)。
  4. 比较操作:提供 operator==operator!=,用于判断两个分配器是否可互换。
示例代码
#include 
#include 

template <typename T>
struct MyAllocator {
    using value_type = T;

    MyAllocator() = default;

    template <typename U>
    MyAllocator(const MyAllocator<U>&) {}

    T* allocate(std::size_t n) {
        return static_cast<T*>(::operator new(n * sizeof(T)));
    }

    void deallocate(T* p, std::size_t n) {
        ::operator delete(p);
    }
};

// 比较操作
template <typename T, typename U>
bool operator==(const MyAllocator<T>&, const MyAllocator<U>&) {
    return true;
}

template <typename T, typename U>
bool operator!=(const MyAllocator<T>&, const MyAllocator<U>&) {
    return false;
}

int main() {
    std::vector<int, MyAllocator<int>> v; // 使用自定义分配器
    v.push_back(42);
    return 0;
}
应用场景
  1. 内存池:减少频繁的内存分配/释放开销。
  2. 共享内存:在多进程间共享容器数据。
  3. 对齐内存:确保内存对齐以满足特定硬件需求(如 SIMD 指令)。
注意事项
  1. 无状态分配器:通常自定义分配器应为无状态的(即不包含非静态成员变量),否则可能影响容器的行为。
  2. C++11 改进:C++11 引入了 std::allocator_traits,简化了自定义分配器的实现,只需实现核心功能(如 allocatedeallocate)。

自定义分配器是 STL 中一个高级但强大的工具,适合对性能或内存管理有特殊需求的场景。


分配器特性(Allocator Traits)

分配器特性是C++标准库中的一个模板类,定义在头文件中,用于提供与分配器(Allocator)相关的统一接口。它主要用于提取和操作分配器的各种属性,而不需要直接访问分配器本身的成员。

主要作用
  1. 统一接口:提供一种标准化的方式访问分配器的成员类型和成员函数,即使分配器没有实现某些成员。
  2. 兼容性:允许自定义分配器仅实现必要的部分,其余部分由allocator_traits提供默认实现。
成员类型

allocator_traits包含以下常见的成员类型(假设Alloc是一个分配器类型):

  • value_type:分配的元素类型,等价于Alloc::value_type
  • pointer:指向元素的指针类型,等价于Alloc::pointer(如果存在),否则为value_type*
  • const_pointer:指向常量元素的指针类型,等价于Alloc::const_pointer(如果存在),否则为const value_type*
  • void_pointer:指向void的指针类型,等价于Alloc::void_pointer(如果存在),否则为void*
  • const_void_pointer:指向const void的指针类型,等价于Alloc::const_void_pointer(如果存在),否则为const void*
  • difference_type:指针差值的类型,等价于Alloc::difference_type(如果存在),否则为std::ptrdiff_t
  • size_type:表示大小的类型,等价于Alloc::size_type(如果存在),否则为std::size_t
  • propagate_on_container_copy_assignment:布尔类型,表示容器在拷贝赋值时是否传播分配器,等价于Alloc::propagate_on_container_copy_assignment(如果存在),否则为std::false_type
  • propagate_on_container_move_assignment:布尔类型,表示容器在移动赋值时是否传播分配器,等价于Alloc::propagate_on_container_move_assignment(如果存在),否则为std::false_type
  • propagate_on_container_swap:布尔类型,表示容器在交换时是否传播分配器,等价于Alloc::propagate_on_container_swap(如果存在),否则为std::false_type
成员函数

allocator_traits提供以下静态成员函数(假设aAlloc类型的实例):

  • allocate(a, n):分配内存,等价于a.allocate(n)
  • deallocate(a, p, n):释放内存,等价于a.deallocate(p, n)
  • construct(a, p, args...):在地址p构造对象,等价于a.construct(p, args...)(如果存在),否则使用placement new
  • destroy(a, p):销毁p指向的对象,等价于a.destroy(p)(如果存在),否则调用析构函数。
  • max_size(a):返回分配器支持的最大分配大小,等价于a.max_size()(如果存在),否则返回std::numeric_limits::max() / sizeof(value_type)
  • select_on_container_copy_construction(a):返回拷贝构造容器时应使用的分配器,等价于a.select_on_container_copy_construction()(如果存在),否则返回a
示例代码
#include 
#include 

int main() {
    using Alloc = std::allocator<int>;
    using Traits = std::allocator_traits<Alloc>;

    Alloc alloc;
    int* p = Traits::allocate(alloc, 1); // 分配内存
    Traits::construct(alloc, p, 42);      // 构造对象
    Traits::destroy(alloc, p);            // 销毁对象
    Traits::deallocate(alloc, p, 1);      // 释放内存

    return 0;
}
总结

allocator_traits是C++标准库中用于简化分配器操作的工具,通过提供默认实现和统一接口,使得自定义分配器的编写更加灵活和方便。


6. 实用工具(Utility Components)

对组(pair)

对组(pair)是 C++ 标准模板库(STL)中的一个模板类,用于将两个不同类型的值组合成一个单一的对象。它定义在 头文件中。

基本定义
template <class T1, class T2>
struct pair {
    T1 first;
    T2 second;
};

pair 包含两个公有成员变量:

  • first:存储第一个值,类型为 T1
  • second:存储第二个值,类型为 T2
构造函数

pair 提供了多种构造函数:

  1. 默认构造函数:初始化 firstsecond 为默认值。
    pair();
    
  2. 值初始化构造函数:用给定的值初始化 firstsecond
    pair(const T1& x, const T2& y);
    
  3. 拷贝构造函数:从另一个 pair 对象初始化。
    template<class U, class V> pair(const pair<U, V>& p);
    
常用操作
  1. 创建 pair 对象

    • 直接初始化:
      pair<int, string> p1(1, "one");
      
    • 使用 make_pair 函数(简化创建):
      auto p2 = make_pair(2, "two");
      
  2. 访问成员

    • 通过 .first.second 访问:
      cout << p1.first << " " << p1.second; // 输出: 1 one
      
  3. 比较操作

    • pair 支持比较运算符(==, !=, <, <=, >, >=),按字典序比较(先比较 first,再比较 second)。
应用场景
  • 需要将两个值作为一个单元处理时(如返回两个值的函数)。
  • 作为 mapunordered_map 的键值对(map 的元素是 pair)。
示例代码
#include 
#include 
#include 
using namespace std;

int main() {
    pair<int, string> p(1, "apple");
    cout << p.first << ": " << p.second << endl; // 输出: 1: apple

    auto q = make_pair(2, "banana");
    if (p < q) {
        cout << "p is less than q" << endl; // 会输出,因为 1 < 2
    }
    return 0;
}

元组(tuple)

基本概念

元组(tuple)是 C++ 标准模板库(STL)中的一个容器,用于存储固定大小的、不同类型的元素。它是一个通用的数据结构,类似于结构体,但不需要预先定义成员名称。元组在 头文件中定义。

特点
  1. 异构性:元组可以存储不同类型的元素,例如 intstringdouble 等。
  2. 固定大小:元组的大小在编译时确定,创建后不能动态增加或减少元素。
  3. 顺序访问:元组中的元素可以通过索引(从 0 开始)访问,但索引必须是编译时常量。
基本用法
  1. 创建元组
    使用 std::tuple 模板类创建元组,可以显式指定类型,也可以使用 std::make_tuple 自动推导类型。

    #include 
    #include 
    
    std::tuple<int, std::string, double> t1(1, "hello", 3.14);
    auto t2 = std::make_tuple(2, "world", 6.28);
    
  2. 访问元素
    使用 std::get(tuple) 访问元组中的元素,index 必须是编译时常量。

    int a = std::get<0>(t1);       // 获取第一个元素(1)
    std::string b = std::get<1>(t1); // 获取第二个元素("hello")
    
  3. 结构化绑定(C++17 引入)
    可以使用结构化绑定直接解包元组元素:

    auto [x, y, z] = t1; // x=1, y="hello", z=3.14
    
  4. 比较操作
    元组支持比较运算符(==, !=, <, >, <=, >=),按字典序逐元素比较。

  5. 元组大小
    使用 std::tuple_size::value 获取元组的大小:

    constexpr size_t size = std::tuple_size<decltype(t1)>::value; // 3
    
适用场景
  1. 需要返回多个不同类型的值时(替代结构体或 std::pair)。
  2. 函数参数传递时,需要打包多个不同类型的值。
  3. 需要临时存储异构数据时。
示例代码
#include 
#include 
#include 

int main() {
    // 创建元组
    auto student = std::make_tuple(101, "Alice", 85.5);

    // 访问元素
    int id = std::get<0>(student);
    std::string name = std::get<1>(student);
    double score = std::get<2>(student);

    std::cout << "ID: " << id << ", Name: " << name << ", Score: " << score << std::endl;

    // 结构化绑定(C++17)
    auto [id2, name2, score2] = student;
    std::cout << "ID: " << id2 << ", Name: " << name2 << ", Score: " << score2 << std::endl;

    return 0;
}
注意事项
  1. 元组的索引必须是编译时常量,不能是变量。
  2. 元组的性能优于 std::pair 但稍逊于结构体,适合轻量级使用。
  3. 在 C++17 之前,解包元组需要使用 std::tie 或手动 std::get

类型特性(type_traits)

类型特性是C++标准库中的一个重要组成部分,位于头文件中。它提供了一系列模板类和模板函数,用于在编译时查询和操作类型信息。类型特性主要用于模板元编程,帮助开发者在编译期间做出类型相关的决策。

主要功能
  1. 类型查询:检查类型是否具有某些特性。

    • std::is_integral:检查T是否为整数类型。
    • std::is_floating_point:检查T是否为浮点类型。
    • std::is_pointer:检查T是否为指针类型。
  2. 类型关系:检查类型之间的关系。

    • std::is_same:检查TU是否为同一类型。
    • std::is_base_of:检查Base是否为Derived的基类。
  3. 类型转换:在编译时转换类型。

    • std::remove_const:移除Tconst限定符。
    • std::add_pointer:为T添加指针修饰符。
  4. 条件选择:根据条件选择类型。

    • std::conditional:如果Btrue,则选择T,否则选择F
示例代码
#include 
#include 

int main() {
    // 检查类型是否为整数
    std::cout << std::boolalpha;
    std::cout << "int is integral: " << std::is_integral<int>::value << std::endl;
    std::cout << "float is integral: " << std::is_integral<float>::value << std::endl;

    // 移除const限定符
    typedef std::remove_const<const int>::type NonConstInt;
    NonConstInt x = 10; // x的类型是int,而不是const int

    // 条件选择类型
    typedef std::conditional<true, int, float>::type Type1; // Type1是int
    typedef std::conditional<false, int, float>::type Type2; // Type2是float

    return 0;
}
应用场景
  1. 模板元编程:在编写泛型代码时,根据类型特性选择不同的实现。
  2. 优化:通过类型特性避免不必要的运行时检查。
  3. 类型安全:确保模板参数满足特定条件,增强代码的健壮性。

类型特性是C++模板编程中不可或缺的工具,能够显著提升代码的灵活性和可维护性。


allocator_traits

allocator_traits 是 C++ 标准库中的一个模板类,用于提供对分配器(allocator)的统一访问接口。它定义在 头文件中,主要用于封装和扩展分配器的功能,使得标准库容器可以更灵活地使用不同类型的分配器。

主要作用
  1. 统一接口:无论分配器是否实现了某些成员函数或类型定义,allocator_traits 都提供了一套统一的接口来访问分配器的功能。
  2. 默认行为:如果分配器没有提供某些成员函数或类型,allocator_traits 会提供默认实现。
  3. 兼容性:使得标准库容器可以兼容自定义分配器,即使这些分配器没有完全实现标准分配器的所有要求。
常用成员类型
  • value_type:分配器分配的元素类型。
  • pointer:指向分配元素的指针类型。
  • const_pointer:指向常量元素的指针类型。
  • void_pointer:指向 void 的指针类型。
  • const_void_pointer:指向 const void 的指针类型。
  • difference_type:指针之间的差值类型。
  • size_type:表示大小的无符号整数类型。
常用成员函数
  • allocate:分配内存。
  • deallocate:释放内存。
  • construct:在已分配的内存上构造对象。
  • destroy:销毁对象但不释放内存。
  • max_size:返回分配器可以分配的最大大小。
示例代码
#include 
#include 

int main() {
    std::allocator<int> alloc;
    std::allocator_traits<std::allocator<int>> traits;

    // 分配内存
    int* p = traits.allocate(alloc, 1);

    // 构造对象
    traits.construct(alloc, p, 42);

    std::cout << *p << std::endl; // 输出: 42

    // 销毁对象
    traits.destroy(alloc, p);

    // 释放内存
    traits.deallocate(alloc, p, 1);

    return 0;
}
注意事项
  • allocator_traits 主要用于标准库内部实现,普通用户代码通常不需要直接使用它。
  • 自定义分配器时,可以通过 allocator_traits 来确保与标准库容器的兼容性。

通过 allocator_traits,C++ 标准库能够更灵活地处理不同类型的内存分配器,同时保持代码的一致性和可维护性。


异常处理(std::exception)

基本概念

std::exception 是 C++ 标准库中所有异常类的基类,定义在 头文件中。它提供了一种统一的机制来处理程序运行时可能发生的错误或异常情况。

主要成员函数
  1. what()
    这是一个虚函数,返回一个描述异常的 C 风格字符串(const char*)。派生类可以重写此函数以提供更具体的错误信息。
    示例:
    try {
        // 可能抛出异常的代码
    } catch (const std::exception& e) {
        std::cerr << "Exception caught: " << e.what() << std::endl;
    }
    
常见派生类

虽然你要求不扩展其他概念,但为了完整性,以下是标准库中一些直接或间接继承自 std::exception 的常见异常类(仅列举名称):

  • std::runtime_error
  • std::logic_error
  • std::bad_alloc(内存分配失败时抛出)
自定义异常

可以通过继承 std::exception 来定义自己的异常类:

class MyException : public std::exception {
public:
    const char* what() const noexcept override {
        return "Custom exception occurred";
    }
};
注意事项
  • 使用 noexcept 修饰符可以标记函数是否可能抛出异常(C++11 引入)。
  • 避免在析构函数中抛出异常,否则可能导致程序终止(通过 std::terminate)。

7. 数值库(Numerical Library)

通用数值算法

通用数值算法是C++标准模板库(STL)中的一组算法,主要用于对数值序列进行操作和计算。这些算法定义在 头文件中,通常与容器(如 vectorarray 等)一起使用。以下是常见的通用数值算法:

1. std::accumulate
  • 功能:计算序列中所有元素的累加和(或自定义二元操作的累积结果)。
  • 语法
    T accumulate(InputIt first, InputIt last, T init);
    T accumulate(InputIt first, InputIt last, T init, BinaryOperation op);
    
  • 参数
    • firstlast:输入序列的迭代器范围。
    • init:初始值。
    • op(可选):二元操作函数(如乘法、减法等)。
  • 示例
    std::vector<int> nums = {1, 2, 3, 4};
    int sum = std::accumulate(nums.begin(), nums.end(), 0); // 输出 10
    int product = std::accumulate(nums.begin(), nums.end(), 1, std::multiplies<int>()); // 输出 24
    
2. std::inner_product
  • 功能:计算两个序列的内积(点积)或自定义操作的结果。
  • 语法
    T inner_product(InputIt1 first1, InputIt1 last1, InputIt2 first2, T init);
    T inner_product(InputIt1 first1, InputIt1 last1, InputIt2 first2, T init, BinaryOperation1 op1, BinaryOperation2 op2);
    
  • 参数
    • first1last1:第一个序列的迭代器范围。
    • first2:第二个序列的起始迭代器(长度需匹配第一个序列)。
    • init:初始值。
    • op1(可选):替换加法的二元操作。
    • op2(可选):替换乘法的二元操作。
  • 示例
    std::vector<int> a = {1, 2, 3};
    std::vector<int> b = {4, 5, 6};
    int dot = std::inner_product(a.begin(), a.end(), b.begin(), 0); // 输出 1*4 + 2*5 + 3*6 = 32
    
3. std::partial_sum
  • 功能:计算序列的部分和(前n个元素的累加结果)。
  • 语法
    OutputIt partial_sum(InputIt first, InputIt last, OutputIt d_first);
    OutputIt partial_sum(InputIt first, InputIt last, OutputIt d_first, BinaryOperation op);
    
  • 参数
    • firstlast:输入序列的迭代器范围。
    • d_first:输出序列的起始迭代器。
    • op(可选):自定义二元操作(默认为加法)。
  • 示例
    std::vector<int> nums = {1, 2, 3, 4};
    std::vector<int> result(4);
    std::partial_sum(nums.begin(), nums.end(), result.begin()); // 输出 {1, 3, 6, 10}
    
4. std::adjacent_difference
  • 功能:计算序列中相邻元素的差值(或自定义操作的结果)。
  • 语法
    OutputIt adjacent_difference(InputIt first, InputIt last, OutputIt d_first);
    OutputIt adjacent_difference(InputIt first, InputIt last, OutputIt d_first, BinaryOperation op);
    
  • 参数
    • firstlast:输入序列的迭代器范围。
    • d_first:输出序列的起始迭代器。
    • op(可选):自定义二元操作(默认为减法)。
  • 示例
    std::vector<int> nums = {2, 4, 6, 8};
    std::vector<int> result(4);
    std::adjacent_difference(nums.begin(), nums.end(), result.begin()); // 输出 {2, 2, 2, 2}
    
5. std::iota
  • 功能:用连续递增的值填充序列。
  • 语法
    void iota(ForwardIt first, ForwardIt last, T value);
    
  • 参数
    • firstlast:目标序列的迭代器范围。
    • value:起始值(每次递增后赋值)。
  • 示例
    std::vector<int> nums(5);
    std::iota(nums.begin(), nums.end(), 10); // 输出 {10, 11, 12, 13, 14}
    
注意事项
  • 所有算法均要求迭代器范围有效,且输出序列有足够空间。
  • 自定义操作函数需满足结合律(如 accumulate)或无副作用。

随机数生成器(Random Number Generator)

在C++ STL中,随机数生成器用于生成伪随机数,主要由头文件提供。它比传统的rand()函数更灵活、更强大,支持多种分布类型和随机数引擎。

1. 随机数引擎

随机数引擎是生成随机数的核心组件,常见的引擎包括:

  • std::default_random_engine:默认的随机数引擎,实现可能因编译器而异。
  • std::mt19937:基于梅森旋转算法的高质量随机数引擎(32位)。
  • std::mt19937_64:64位版本的梅森旋转引擎。
2. 随机数分布

随机数分布用于将引擎生成的随机数映射到特定的范围内或按特定概率分布生成。常见的分布包括:

  • 均匀分布
    • std::uniform_int_distribution:生成均匀分布的整数。
    • std::uniform_real_distribution:生成均匀分布的浮点数。
  • 正态分布std::normal_distribution
  • 伯努利分布std::bernoulli_distribution(生成truefalse)。
3. 基本用法
#include 
#include 

int main() {
    // 1. 初始化随机数引擎
    std::random_device rd;  // 用于获取随机种子
    std::mt19937 gen(rd()); // 使用梅森旋转引擎

    // 2. 定义分布(生成1到6的均匀整数,模拟骰子)
    std::uniform_int_distribution<> dis(1, 6);

    // 3. 生成随机数
    for (int i = 0; i < 10; ++i) {
        std::cout << dis(gen) << " ";
    }
    return 0;
}
4. 注意事项
  • 种子初始化:使用std::random_device获取真随机数作为种子,避免伪随机序列重复。
  • 线程安全:随机数引擎和分布不是线程安全的,需为每个线程单独实例化。
  • 性能std::mt19937适合大多数场景,但初始化较慢。
5. 与传统rand()的区别
  • 更灵活的分布控制。
  • 更高的随机性和质量。
  • 可复现性(通过固定种子)。

数值数组(valarray)

valarray 是 C++ 标准模板库(STL)中的一个类模板,专门用于高效处理数值计算。它提供了对数值数组的支持,包括数学运算、切片、间接访问等功能,适用于科学计算和数值分析。

主要特点
  1. 数学运算优化

    • valarray 重载了常见的数学运算符(如 +, -, *, /),可以直接对整个数组进行逐元素运算。
    • 支持数学函数(如 sin, cos, sqrt)的逐元素操作。
  2. 切片和间接访问

    • 支持 slice(切片)和 gslice(广义切片)操作,可以高效地访问数组的子集。
    • 提供 mask_arrayindirect_array 用于条件筛选和间接索引访问。
  3. 内存连续性

    • valarray 通常以连续内存块存储数据,适合与 C 风格数组或低级数值库(如 BLAS)交互。
基本用法
#include 
#include 

int main() {
    std::valarray<int> v1 = {1, 2, 3, 4, 5};
    std::valarray<int> v2 = {5, 4, 3, 2, 1};

    // 逐元素加法
    std::valarray<int> v3 = v1 + v2;

    // 输出结果
    for (int n : v3) {
        std::cout << n << ' ';
    }
    // 输出:6 6 6 6 6
}
常用成员函数
  • size():返回数组的大小。
  • sum():计算所有元素的和。
  • min() / max():返回最小/最大值。
  • apply(func):对每个元素应用函数 func
注意事项
  • valarray 的设计目标是高性能数值计算,但某些操作(如动态调整大小)可能不如 vector 灵活。
  • 部分编译器对 valarray 的实现优化不足,实际性能可能因平台而异。

valarray 适合需要频繁进行数值运算的场景,但在通用性上不如 vectorarray


你可能感兴趣的:(c++,开发语言)