一、线性容器:std::array 与 std::forward_list
1. std::array:固定大小的高效容器
在传统 C++ 中,数组与 vector 的抉择常让人纠结:数组缺乏安全检查,vector 存在动态扩容开销。C++11 引入的std::array
完美平衡了两者优势:
-
特性解析:
- 编译期确定大小,内存连续分配,访问效率与 C 数组一致;
- 封装了迭代器、size ()、empty () 等标准接口,兼容 STL 算法;
- 避免 vector 扩容时的重分配开销,适合已知容量的场景。
-
代码示例:
#include <array>
#include <iostream>
#include <algorithm>int main() {// 初始化与基本操作std::array<int, 4> arr = {1, 3, 2, 4};std::cout << "数组大小:" << arr.size() << std::endl;// 迭代器与算法支持std::sort(arr.begin(), arr.end());for (const auto& num : arr) {std::cout << num << " ";}// 与C接口兼容int* c_ptr = arr.data();return 0;
}
- 使用场景:
- 存储固定长度的配置项(如哈希表桶数量);
- 替代局部 C 数组,避免越界风险;
- 作为函数参数传递时,避免退化为指针导致的长度丢失。
2. std::forward_list:单向链表的轻量选择
与双向链表std::list
相比,std::forward_list
采用单向链表实现,牺牲反向遍历能力换取更紧凑的内存布局:
-
核心优势:
- 节点仅含 next 指针,空间利用率比 list 高约 30%;
- 支持 O (1) 复杂度的头部插入 / 删除;
- 无 size () 方法(需遍历计算长度),适合 “添加 - 遍历” 场景。
-
典型应用:
#include <forward_list>int main() {std::forward_list<int> flist;flist.push_front(1);flist.push_front(2);// 遍历与删除auto it = flist.begin();if (it != flist.end()) {flist.erase_after(it); // 删除头节点后的元素}// 合并链表std::forward_list<int> another = {3, 4};flist.merge(another);return 0;
}
二、无序容器:哈希表的标准化实现
传统std::map
/std::set
基于红黑树实现,插入与查找复杂度为 O (logN)。C++11 引入的无序容器基于哈希表,平均复杂度降至 O (1):
1. 接口与性能对比
以std::unordered_map
为例,与std::map
的关键差异:
特性 | std::map | std::unordered_map |
---|---|---|
底层结构 | 红黑树 | 哈希表 + 链表(解决冲突) |
插入复杂度 | O(logN) | 平均 O (1),最坏 O (N) |
遍历顺序 | 按键有序 | 无固定顺序 |
内存开销 | 每个节点含左右指针 | 哈希桶 + 链表指针 |
适用场景 | 需有序遍历、范围查询 | 高频查找、无序存储 |
2. 实战技巧
- 哈希函数定制:
#include <unordered_map>
#include <string>struct Person {std::string name;int age;bool operator==(const Person& other) const {return name == other.name && age == other.age;}
};// 为自定义类型特化哈希函数
namespace std {template<>struct hash<Person> {size_t operator()(const Person& p) const {return hash<string>()(p.name) ^ (hash<int>()(p.age) << 1);}};
}int main() {std::unordered_map<Person, string> person_map;return 0;
}
- 性能优化点:
- 预分配桶数量:
reserve(n)
避免动态扩容导致的重哈希; - 减少哈希冲突:选择分布均匀的哈希函数,或使用
std::unordered_map
的max_load_factor
调整负载因子; - 避免频繁修改键值:修改键值可能导致哈希位置变化,需重新插入。
- 预分配桶数量:
三、元组 std::tuple:多类型数据的聚合神器
传统std::pair
仅能存储两个元素,std::tuple
则支持任意数量、任意类型的元素组合:
1. 基础操作与解包
#include <tuple>
#include <iostream>int main() {// 创建元组auto student = std::make_tuple(95, 'A', "张三");// 访问元素(编译期索引)int score = std::get<0>(student);char grade = std::get<1>(student);// 结构化绑定(C++17特性)auto [s, g, name] = student;std::cout << "姓名:" << name << ",成绩:" << s << std::endl;// 元组合并auto new_tuple = std::tuple_cat(student, std::make_tuple(18));return 0;
}
2. 运行期索引与泛型处理
C++17 引入的std::variant
配合元组,实现运行期动态索引:
#include <variant>
#include <tuple>
#include <iostream>// 运行期索引元组元素
template <size_t N, typename... T>
constexpr std::variant<T...> tuple_at(const std::tuple<T...>& tpl, size_t index) {if constexpr (N >= sizeof...(T)) {throw std::out_of_range("索引越界");}if (index == N) {return std::variant<T...>{std::in_place_index<N>, std::get<N>(tpl)};}return tuple_at<(N < sizeof...(T) - 1 ? N + 1 : 0)>(tpl, index);
}int main() {auto t = std::make_tuple(10, "hello", 3.14);size_t idx = 1;std::visit([](auto&& x) { std::cout << x << std::endl; }, tuple_at<0>(t, idx));return 0;
}
3. 实用场景
- 函数多返回值(替代结构体或 pair 嵌套);
- 数据库记录映射(一行数据映射为元组);
- 泛型编程中的参数包处理(如日志函数的可变参数格式化)。
四、容器选择与性能优化
-
按场景选容器:
- 需有序遍历:
std::map
/std::set
; - 高频查找:
std::unordered_map
/std::unordered_set
; - 固定大小数组:
std::array
; - 频繁头部插入:
std::forward_list
。
- 需有序遍历:
-
性能优化 Tips:
std::vector
预留空间:reserve()
避免多次扩容;- 优先使用
emplace_back
替代push_back
+ 构造; - 对
std::unordered_map
,合理设置哈希函数与负载因子; - 元组作为函数返回值时,利用
std::move
避免拷贝。