第22章 STL标准模板库

STL核心组件概览

STL(Standard Template Library)是C++标准库的核心组成部分,提供了一系列通用的模板类和函数,实现了常见的数据结构和算法。STL的设计理念是”泛型编程”,通过模板技术实现了代码的高度复用。

STL主要由以下六个核心组件组成:

  1. 容器(Containers):存储数据的对象,如向量、链表、集合、映射等
  2. 迭代器(Iterators):提供访问容器中元素的方法,行为类似指针
  3. 算法(Algorithms):操作容器元素的函数,如排序、查找、变换等
  4. 函数对象(Functors):重载了operator()的类对象,用于定制算法行为
  5. 适配器(Adapters):修改其他STL组件接口的组件,如栈、队列、优先级队列
  6. 分配器(Allocators):负责内存分配和管理的组件

容器深度解析

序列容器

序列容器按照线性顺序存储元素,支持通过位置访问。

std::vector

std::vector是最常用的序列容器,它使用动态数组实现,支持快速随机访问。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <vector>
#include <iostream>

int main() {
// 创建vector
std::vector<int> v1;
std::vector<int> v2(5, 10); // 5个元素,每个值为10
std::vector<int> v3 = {1, 2, 3, 4, 5};

// 添加元素
v1.push_back(1);
v1.push_back(2);

// 访问元素
std::cout << v3[2] << std::endl; // 3
std::cout << v3.at(2) << std::endl; // 3,带边界检查

// 遍历
for (auto it = v3.begin(); it != v3.end(); ++it) {
std::cout << *it << " ";
}
std::cout << std::endl;

// C++11范围for
for (int x : v3) {
std::cout << x << " ";
}
std::cout << std::endl;

return 0;
}

性能特性

  • 随机访问:O(1)
  • 在尾部插入/删除:均摊O(1)
  • 在中间或头部插入/删除:O(n)
  • 内存布局:连续存储,缓存友好

std::list

std::list是双向链表实现的容器,支持常数时间的插入和删除操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <list>
#include <algorithm>

int main() {
std::list<int> lst = {5, 2, 9, 1, 5, 6};

// 排序
lst.sort();

// 插入
auto it = lst.begin();
++it;
lst.insert(it, 10);

// 删除
lst.remove(5); // 删除所有值为5的元素

// 合并
std::list<int> lst2 = {3, 7, 8};
lst.merge(lst2);

return 0;
}

性能特性

  • 随机访问:O(n)
  • 在任意位置插入/删除:O(1)(已知位置)
  • 内存布局:节点分散存储,每个节点包含前后指针

std::forward_list

std::forward_list是C++11引入的单向链表,比std::list更节省空间。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <forward_list>

int main() {
std::forward_list<int> flist = {1, 2, 3, 4, 5};

// 插入
flist.push_front(0);

// 删除
flist.pop_front();

// 插入到指定位置
auto it = flist.begin();
flist.insert_after(it, 10);

return 0;
}

性能特性

  • 只支持前向遍历
  • 内存开销小于std::list
  • 插入/删除操作与std::list类似

std::deque

std::deque(双端队列)支持在两端高效插入和删除元素,同时支持随机访问。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <deque>

int main() {
std::deque<int> dq = {1, 2, 3};

// 两端插入
dq.push_back(4);
dq.push_front(0);

// 两端删除
dq.pop_back();
dq.pop_front();

return 0;
}

性能特性

  • 随机访问:O(1)
  • 两端插入/删除:O(1)
  • 中间插入/删除:O(n)
  • 内存布局:分段连续存储

关联容器

关联容器使用键值对存储元素,支持基于键的快速查找。

std::setstd::multiset

std::set存储唯一键,std::multiset允许重复键,两者都按升序排列。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <set>

int main() {
std::set<int> s = {3, 1, 4, 1, 5, 9};

// 插入
s.insert(2);

// 查找
auto it = s.find(3);
if (it != s.end()) {
// 找到元素
}

// 计数
size_t count = s.count(1); // 1,因为set中元素唯一

return 0;
}

性能特性

  • 插入/删除/查找:O(log n)
  • 基于平衡二叉搜索树实现

std::mapstd::multimap

std::map存储键值对,键唯一;std::multimap允许重复键。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <map>

int main() {
std::map<std::string, int> m;

// 插入
m["one"] = 1;
m.insert({"two", 2});

// 查找
auto it = m.find("one");
if (it != m.end()) {
std::cout << it->first << ": " << it->second << std::endl;
}

return 0;
}

性能特性

  • 插入/删除/查找:O(log n)
  • 基于平衡二叉搜索树实现

无序关联容器

C++11引入的无序关联容器使用哈希表实现,提供平均常数时间的操作。

std::unordered_setstd::unordered_multiset

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <unordered_set>

int main() {
std::unordered_set<int> us = {3, 1, 4, 1, 5, 9};

// 插入
us.insert(2);

// 查找
auto it = us.find(3);

return 0;
}

性能特性

  • 平均插入/删除/查找:O(1)
  • 最坏情况:O(n)
  • 基于哈希表实现

std::unordered_mapstd::unordered_multimap

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <unordered_map>

int main() {
std::unordered_map<std::string, int> um;

// 插入
um["one"] = 1;

// 查找
auto it = um.find("one");

return 0;
}

性能特性

  • 平均插入/删除/查找:O(1)
  • 最坏情况:O(n)
  • 基于哈希表实现

容器适配器

容器适配器修改现有容器的接口,提供特定的功能。

std::stack

std::stack提供后进先出(LIFO)的操作接口。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <stack>

int main() {
std::stack<int> s;

// 入栈
s.push(1);
s.push(2);

// 查看栈顶
std::cout << s.top() << std::endl; // 2

// 出栈
s.pop();

return 0;
}

std::queue

std::queue提供先进先出(FIFO)的操作接口。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <queue>

int main() {
std::queue<int> q;

// 入队
q.push(1);
q.push(2);

// 查看队首
std::cout << q.front() << std::endl; // 1

// 出队
q.pop();

return 0;
}

std::priority_queue

std::priority_queue提供优先级队列,顶部元素总是最大的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <queue>

int main() {
std::priority_queue<int> pq;

// 入队
pq.push(3);
pq.push(1);
pq.push(4);

// 查看队首(最大值)
std::cout << pq.top() << std::endl; // 4

// 出队
pq.pop();

return 0;
}

迭代器深度解析

迭代器是连接容器和算法的桥梁,提供了一种统一的方式访问容器中的元素。

迭代器类别

STL定义了五种迭代器类别,按功能从弱到强排序:

  1. 输入迭代器(Input Iterators):只读,只能单向移动,只能访问一次
  2. 输出迭代器(Output Iterators):只写,只能单向移动,只能访问一次
  3. 前向迭代器(Forward Iterators):可读写,只能单向移动,可多次访问
  4. 双向迭代器(Bidirectional Iterators):可读写,可双向移动,可多次访问
  5. 随机访问迭代器(Random Access Iterators):可读写,可随机访问,可多次访问

迭代器操作

操作输入迭代器输出迭代器前向迭代器双向迭代器随机访问迭代器
++it
*it✅ (读)✅ (写)
it++
it1 == it2
it1 != it2
--it
it--
it + n
it - n
it += n
it -= n
it1 < it2
it1 > it2
it1 <= it2
it1 >= it2
it1 - it2
it[n]

迭代器适配器

STL提供了几种特殊的迭代器适配器:

std::reverse_iterator

反向迭代器,用于反向遍历容器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <vector>
#include <iostream>

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

// 反向遍历
for (auto it = v.rbegin(); it != v.rend(); ++it) {
std::cout << *it << " "; // 5 4 3 2 1
}
std::cout << std::endl;

return 0;
}

std::insert_iterator

插入迭代器,用于在容器中插入元素。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <vector>
#include <iterator>
#include <algorithm>

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

// 插入到v1末尾
std::copy(v2.begin(), v2.end(), std::back_inserter(v1));

// 插入到指定位置
std::copy(v2.begin(), v2.end(), std::inserter(v1, v1.begin()));

return 0;
}

std::move_iterator

移动迭代器,用于移动元素而非复制。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <vector>
#include <iterator>
#include <string>

int main() {
std::vector<std::string> v1 = {"one", "two", "three"};
std::vector<std::string> v2;

// 移动元素
std::move(std::make_move_iterator(v1.begin()),
std::make_move_iterator(v1.end()),
std::back_inserter(v2));

return 0;
}

算法深度解析

STL算法是一组独立于容器的函数模板,用于操作容器中的元素。

遍历算法

std::for_each

对容器中的每个元素应用指定操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <vector>
#include <algorithm>
#include <iostream>

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

// 使用函数对象
std::for_each(v.begin(), v.end(), [](int& x) {
x *= 2;
std::cout << x << " "; // 2 4 6 8 10
});
std::cout << std::endl;

return 0;
}

查找算法

std::find

查找指定值的第一个出现位置。

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <vector>
#include <algorithm>

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

auto it = std::find(v.begin(), v.end(), 3);
if (it != v.end()) {
// 找到元素
}

return 0;
}

std::find_if

查找满足条件的第一个元素。

1
2
3
4
5
6
7
8
9
10
11
12
#include <vector>
#include <algorithm>

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

auto it = std::find_if(v.begin(), v.end(), [](int x) {
return x > 3;
});

return 0;
}

在有序范围内二分查找元素。

1
2
3
4
5
6
7
8
9
10
#include <vector>
#include <algorithm>

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

bool found = std::binary_search(v.begin(), v.end(), 3);

return 0;
}

排序算法

std::sort

对元素进行排序。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <vector>
#include <algorithm>

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

// 升序排序
std::sort(v.begin(), v.end());

// 降序排序
std::sort(v.begin(), v.end(), std::greater<int>());

// 自定义排序
std::sort(v.begin(), v.end(), [](int a, int b) {
return abs(a) < abs(b);
});

return 0;
}

std::stable_sort

稳定排序,保持相等元素的相对顺序。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <vector>
#include <algorithm>

struct Person {
std::string name;
int age;
};

int main() {
std::vector<Person> people = {
{"Alice", 25},
{"Bob", 30},
{"Charlie", 25},
{"David", 30}
};

// 按年龄排序,保持姓名顺序
std::stable_sort(people.begin(), people.end(), [](const Person& a, const Person& b) {
return a.age < b.age;
});

return 0;
}

变换算法

std::transform

对元素进行变换并存储结果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <vector>
#include <algorithm>

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

// 变换元素
std::transform(v1.begin(), v1.end(), v2.begin(), [](int x) {
return x * x;
});

// 二元变换
std::vector<int> v3 = {10, 20, 30, 40, 50};
std::vector<int> v4(v1.size());
std::transform(v1.begin(), v1.end(), v3.begin(), v4.begin(), [](int a, int b) {
return a + b;
});

return 0;
}

数值算法

std::accumulate

计算元素的累积和。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <vector>
#include <numeric>

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

// 计算和
int sum = std::accumulate(v.begin(), v.end(), 0); // 15

// 计算乘积
int product = std::accumulate(v.begin(), v.end(), 1, [](int a, int b) {
return a * b;
}); // 120

return 0;
}

std::iota

填充递增序列。

1
2
3
4
5
6
7
8
9
10
11
#include <vector>
#include <numeric>

int main() {
std::vector<int> v(5);

// 填充0, 1, 2, 3, 4
std::iota(v.begin(), v.end(), 0);

return 0;
}

集合算法

std::set_union

计算两个有序范围的并集。

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <vector>
#include <algorithm>

int main() {
std::vector<int> v1 = {1, 3, 5, 7, 9};
std::vector<int> v2 = {2, 4, 6, 8, 10};
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());

return 0;
}

std::set_intersection

计算两个有序范围的交集。

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <vector>
#include <algorithm>

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

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

return 0;
}

函数对象与适配器

预定义函数对象

STL在<functional>头文件中定义了一系列预定义函数对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <functional>

int main() {
// 算术运算
std::plus<int> add;
std::minus<int> subtract;
std::multiplies<int> multiply;
std::divides<int> divide;
std::modulus<int> mod;
std::negate<int> negate;

// 比较运算
std::equal_to<int> equal;
std::not_equal_to<int> not_equal;
std::greater<int> greater;
std::less<int> less;
std::greater_equal<int> greater_equal;
std::less_equal<int> less_equal;

// 逻辑运算
std::logical_and<bool> logical_and;
std::logical_or<bool> logical_or;
std::logical_not<bool> logical_not;

return 0;
}

函数适配器

std::bind

std::bind用于绑定函数参数,创建新的可调用对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <functional>

int add(int a, int b, int c) {
return a + b + c;
}

int main() {
// 绑定前两个参数
auto add5and10 = std::bind(add, 5, 10, std::placeholders::_1);
int result1 = add5and10(20); // 35

// 绑定第三个参数
auto addWith100 = std::bind(add, std::placeholders::_1, std::placeholders::_2, 100);
int result2 = addWith100(10, 20); // 130

return 0;
}

std::function

std::function是一个通用的函数包装器,可以存储任何可调用对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <functional>

int add(int a, int b) {
return a + b;
}

class Multiply {
public:
int operator()(int a, int b) const {
return a * b;
}
};

int main() {
// 包装函数指针
std::function<int(int, int)> f1 = add;

// 包装函数对象
Multiply multiply;
std::function<int(int, int)> f2 = multiply;

// 包装Lambda表达式
std::function<int(int, int)> f3 = [](int a, int b) {
return a - b;
};

return 0;
}

STL性能优化策略

容器选择策略

场景推荐容器原因
频繁随机访问std::vectorO(1)随机访问,缓存友好
频繁在两端插入/删除std::dequeO(1)两端操作
频繁在任意位置插入/删除std::listO(1)插入/删除(已知位置)
频繁查找std::unordered_set/std::unordered_map平均O(1)查找
需要有序容器std::set/std::map自动排序,O(log n)操作
需要优先级队列std::priority_queue自动维护最大元素在顶部

内存管理优化

预留空间

对于std::vectorstd::string等容器,使用reserve预分配空间可以避免频繁的内存重分配:

1
2
3
4
5
6
std::vector<int> v;
v.reserve(1000); // 预分配1000个元素的空间

for (int i = 0; i < 1000; ++i) {
v.push_back(i); // 不会触发内存重分配
}

移动语义

使用移动语义可以避免不必要的复制操作:

1
2
3
std::vector<std::string> v;
std::string s = "Hello";
v.push_back(std::move(s)); // 移动而非复制

自定义分配器

对于特殊场景,可以使用自定义分配器来优化内存管理:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <vector>
#include <memory>

// 简单的内存池分配器
template <typename T>
class PoolAllocator {
public:
using value_type = T;

PoolAllocator() = default;

template <typename U>
PoolAllocator(const PoolAllocator<U>&) noexcept {}

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

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

int main() {
std::vector<int, PoolAllocator<int>> v;
v.push_back(1);

return 0;
}

算法优化

选择合适的算法

根据具体需求选择最适合的算法:

  • 对于有序范围,使用std::binary_search而不是std::find
  • 对于需要保持顺序的排序,使用std::stable_sort
  • 对于不需要稳定排序的场景,使用std::sort(通常更快)

使用移动迭代器

对于需要转移所有权的场景,使用std::move_iterator

1
2
3
4
5
6
7
std::vector<std::string> v1 = {"one", "two", "three"};
std::vector<std::string> v2;

// 移动元素,避免复制
std::move(std::make_move_iterator(v1.begin()),
std::make_move_iterator(v1.end()),
std::back_inserter(v2));

并行算法

C++17引入了并行算法,利用多核心提高性能:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <vector>
#include <algorithm>
#include <execution>

int main() {
std::vector<int> v(1000000);

// 并行填充
std::fill(std::execution::par, v.begin(), v.end(), 42);

// 并行排序
std::sort(std::execution::par, v.begin(), v.end());

return 0;
}

STL高级技巧

自定义迭代器

为自定义容器实现迭代器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include <iterator>

class CustomContainer {
private:
int data[10];
public:
class Iterator : public std::iterator<std::random_access_iterator_tag, int> {
private:
int* ptr;
public:
Iterator(int* p) : ptr(p) {}

int& operator*() const { return *ptr; }
int* operator->() const { return ptr; }

Iterator& operator++() { ++ptr; return *this; }
Iterator operator++(int) { Iterator tmp = *this; ++ptr; return tmp; }

Iterator& operator--() { --ptr; return *this; }
Iterator operator--(int) { Iterator tmp = *this; --ptr; return tmp; }

Iterator& operator+=(std::ptrdiff_t n) { ptr += n; return *this; }
Iterator operator+(std::ptrdiff_t n) const { return Iterator(ptr + n); }

Iterator& operator-=(std::ptrdiff_t n) { ptr -= n; return *this; }
Iterator operator-(std::ptrdiff_t n) const { return Iterator(ptr - n); }

std::ptrdiff_t operator-(const Iterator& other) const { return ptr - other.ptr; }

int& operator[](std::ptrdiff_t n) const { return ptr[n]; }

bool operator==(const Iterator& other) const { return ptr == other.ptr; }
bool operator!=(const Iterator& other) const { return ptr != other.ptr; }
bool operator<(const Iterator& other) const { return ptr < other.ptr; }
bool operator<=(const Iterator& other) const { return ptr <= other.ptr; }
bool operator>(const Iterator& other) const { return ptr > other.ptr; }
bool operator>=(const Iterator& other) const { return ptr >= other.ptr; }
};

Iterator begin() { return Iterator(data); }
Iterator end() { return Iterator(data + 10); }
};

扩展STL算法

通过函数对象和Lambda表达式扩展STL算法的功能:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <vector>
#include <algorithm>

// 自定义谓词
struct IsEven {
bool operator()(int x) const {
return x % 2 == 0;
}
};

int main() {
std::vector<int> v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

// 使用自定义谓词
int count = std::count_if(v.begin(), v.end(), IsEven());

// 使用Lambda表达式
count = std::count_if(v.begin(), v.end(), [](int x) {
return x % 2 == 0;
});

return 0;
}

元编程与STL

使用模板元编程技术增强STL的功能:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <vector>
#include <type_traits>

// 类型特性检测
template <typename Container>
struct is_associative_container {
static constexpr bool value = false;
};

template <typename Key, typename T, typename Compare, typename Alloc>
struct is_associative_container<std::map<Key, T, Compare, Alloc>> {
static constexpr bool value = true;
};

// 容器特性函数
template <typename Container>
void process_container(Container& c) {
if constexpr (is_associative_container<Container>::value) {
// 处理关联容器
} else {
// 处理序列容器
}
}

实际应用案例

案例1:高效数据处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <vector>
#include <algorithm>
#include <numeric>
#include <execution>

// 计算一组数据的统计信息
struct Statistics {
double mean;
double median;
double std_dev;
};

Statistics compute_statistics(std::vector<double> data) {
Statistics stats;

// 计算均值
double sum = std::accumulate(data.begin(), data.end(), 0.0);
stats.mean = sum / data.size();

// 计算中位数
std::sort(std::execution::par, data.begin(), data.end());
if (data.size() % 2 == 0) {
stats.median = (data[data.size() / 2 - 1] + data[data.size() / 2]) / 2;
} else {
stats.median = data[data.size() / 2];
}

// 计算标准差
double squared_diff_sum = 0.0;
for (double x : data) {
squared_diff_sum += (x - stats.mean) * (x - stats.mean);
}
stats.std_dev = std::sqrt(squared_diff_sum / data.size());

return stats;
}

案例2:事件系统

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include <functional>
#include <vector>
#include <map>

class EventSystem {
public:
using EventHandler = std::function<void()>;

void subscribe(const std::string& event, EventHandler handler) {
handlers[event].push_back(std::move(handler));
}

void publish(const std::string& event) {
auto it = handlers.find(event);
if (it != handlers.end()) {
for (auto& handler : it->second) {
handler();
}
}
}

private:
std::map<std::string, std::vector<EventHandler>> handlers;
};

int main() {
EventSystem event_system;

// 订阅事件
event_system.subscribe("click", []() {
std::cout << "Click event triggered!" << std::endl;
});

// 发布事件
event_system.publish("click");

return 0;
}

案例3:缓存系统

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#include <unordered_map>
#include <functional>
#include <mutex>
#include <shared_mutex>

// 线程安全的缓存
template <typename Key, typename Value, typename Hash = std::hash<Key>>
class Cache {
public:
using ValueProvider = std::function<Value(const Key&)>;

Cache(ValueProvider provider) : provider(std::move(provider)) {}

Value get(const Key& key) {
// 共享锁读取
std::shared_lock<std::shared_mutex> read_lock(mutex);
auto it = cache.find(key);
if (it != cache.end()) {
return it->second;
}
read_lock.unlock();

// 唯一锁写入
std::unique_lock<std::shared_mutex> write_lock(mutex);
// 双重检查
it = cache.find(key);
if (it != cache.end()) {
return it->second;
}

// 计算值并缓存
Value value = provider(key);
cache[key] = value;
return value;
}

void clear() {
std::unique_lock<std::shared_mutex> lock(mutex);
cache.clear();
}

private:
std::unordered_map<Key, Value, Hash> cache;
ValueProvider provider;
mutable std::shared_mutex mutex;
};

最佳实践与注意事项

1. 容器使用最佳实践

  • 选择合适的容器:根据访问模式和操作需求选择最适合的容器
  • 避免不必要的复制:使用移动语义和引用传递减少复制开销
  • 合理使用reserve:对于std::vector等容器,预分配足够的空间
  • 注意容器的线程安全性:STL容器默认不是线程安全的,需要手动同步
  • 避免存储大对象:对于大对象,考虑存储指针或智能指针

2. 迭代器使用最佳实践

  • 注意迭代器失效:修改容器时注意哪些操作会使迭代器失效
  • 使用erase的返回值erase返回指向下一个元素的迭代器,避免二次失效
  • 优先使用范围for:对于简单遍历,使用范围for循环更简洁
  • 避免在循环中修改容器:如果需要修改,使用迭代器的安全方式

3. 算法使用最佳实践

  • 选择合适的算法:根据具体需求选择最适合的算法
  • 使用并行算法:对于大规模数据处理,考虑使用C++17并行算法
  • 自定义谓词:通过函数对象和Lambda表达式定制算法行为
  • 注意算法的复杂度:了解算法的时间复杂度,避免性能陷阱

4. 性能优化注意事项

  • 测量而非猜测:使用性能分析工具测量瓶颈,而非仅凭直觉优化
  • 优先考虑算法:选择正确的算法比微观优化更重要
  • 利用缓存局部性:设计数据结构时考虑缓存友好性
  • 避免过度优化:在性能瓶颈处进行优化,避免过早优化

总结

STL是C++中最强大、最常用的库之一,它提供了丰富的容器、迭代器、算法和其他组件,大大提高了编程效率和代码质量。通过本章的学习,我们深入了解了STL的核心组件和高级特性,掌握了:

  1. 容器的选择与使用:根据不同场景选择合适的容器,理解各种容器的性能特性
  2. 迭代器的原理与应用:掌握不同类型迭代器的使用方法,理解迭代器适配器的作用
  3. 算法的灵活应用:熟练使用STL算法,通过函数对象和Lambda表达式定制算法行为
  4. 性能优化策略:通过内存管理、容器选择和算法优化提高STL代码性能
  5. 高级技巧与实际应用:通过自定义迭代器、扩展算法和元编程技术增强STL功能

STL的设计理念——泛型编程、分离关注点、算法与容器分离——不仅是C++编程的核心思想,也是现代软件工程的重要原则。通过合理使用STL,我们可以编写更加简洁、高效、可维护的C++代码。

在实际开发中,我们应该不断积累STL的使用经验,结合具体场景选择最合适的STL组件,充分发挥STL的强大功能,同时注意避免常见的陷阱和性能问题。只有这样,才能真正掌握STL这一C++编程的利器。