一、線性容器: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
避免拷貝。