**C++ 中 std::vector 全面解析(從基礎到進階)
std::vector 是 C++ 標準庫(STL)中最常用的動態數組容器,能自動管理內存、動態擴容,比手動用 new[] 分配數組更安全高效,是日常開發的“高頻工具”。下面從基礎用法到進階技巧,帶你吃透它~**
一、基礎:怎麼用 std::vector ?
1. 頭文件與初始化
用 std::vector 前必須包含頭文件 <vector> ,初始化方式有多種,按需選擇:
cpp
include <vector>
include <iostream>
using namespace std; // 簡化代碼,實際項目建議少用
int main() {
// 1. 空向量(最常用,後續動態添加元素)
vector<int> vec1;
// 2. 初始化 5 個元素,每個元素值為 0
vector<int> vec2(5, 0);
// 3. 用列表初始化(C++11 及以後支持)
vector<int> vec3 = {1, 2, 3, 4, 5};
// 4. 拷貝另一個向量
vector<int> vec4(vec3);
return 0;
}
- 核心操作:增刪查改
cpp
vector<int> vec = {1, 2, 3};
// 1. 增加元素(尾插最高效,時間複雜度 O(1))
vec.push_back(4); // 結果:[1,2,3,4]
// C++11 後支持 emplace_back(直接在容器內構造元素,比 push_back 更高效)
vec.emplace_back(5); // 結果:[1,2,3,4,5]
// 2. 刪除元素(尾刪高效,O(1);刪除中間元素需移動數據,O(n))
vec.pop_back(); // 刪除最後一個元素,結果:[1,2,3,4]
// 刪除指定位置元素(用迭代器)
vec.erase(vec.begin() + 1); // 刪除索引 1 的元素(值為 2),結果:[1,3,4]
// 3. 訪問元素(3種方式)
cout << vec[0]; // 1(直接用索引,無越界檢查,快)
cout << vec.at(1); // 3(帶越界檢查,越界會拋異常,安全)
cout << vec.front(); // 1(訪問第一個元素)
cout << vec.back(); // 4(訪問最後一個元素)
// 4. 修改元素
vec[2] = 10; // 結果:[1,3,10]
- 常用屬性
cpp
vector<int> vec = {1,3,10};
cout << vec.size(); // 3(當前元素個數)
cout << vec.capacity(); // 3(當前容器能容納的最大元素數,未擴容時等於 size)
cout << vec.empty(); // false(判斷是否為空,空則返回 true)
vec.clear(); // 清空所有元素(size 變為 0,但 capacity 不變)
大標題
二、進階:理解 std::vector 的核心機制
大標題
- 動態擴容原理
std::vector 底層是連續內存空間,當 push_back 元素超過當前 capacity 時,會觸發“擴容”:
分配一塊更大的新內存(通常是原容量的 2 倍,不同編譯器可能有差異,比如 VS 是 1.5 倍);
把原內存的元素拷貝/移動到新內存;
釋放原內存。
⚠️ **注意:擴容後,原有的迭代器、指針、引用會失效(因為內存地址變了),比如:
cpp
vector<int> vec = {1,2,3};
int* p = &vec[0]; // 指向第一個元素的指針
vec.push_back(4); // 若觸發擴容,p 會變成“野指針”,再用 p 訪問會出錯
2. 避免頻繁擴容:提前 reserve
大標題
如果知道大概要存多少元素,用 reserve(n) 提前分配 n 個容量,避免多次擴容(減少拷貝開銷):
cpp
vector<int> vec;
vec.reserve(100); // 提前分配 100 個容量,後續添加 100 個元素不會擴容
for (int i = 0; i < 100; i++) {
vec.push_back(i);
}
- 迭代器的使用(遍歷/操作)
迭代器是訪問容器的“通用指針”, std::vector 支持隨機訪問迭代器(可像指針一樣加減偏移):
cpp
vector<int> vec = {1,3,10};
// 1. 正向遍歷
for (vector<int>::iterator it = vec.begin(); it != vec.end(); it++) {
cout << *it << " "; // 輸出:1 3 10
}
// 2. 反向遍歷(用 reverse_iterator)
for (vector<int>::reverse_iterator it = vec.rbegin(); it != vec.rend(); it++) {
cout << *it << " "; // 輸出:10 3 1
}
// 3. C++11 後簡化:範圍 for 循環(底層還是迭代器)
for (int num : vec) {
cout << num << " "; // 輸出:1 3 10
}
三、實戰: std::vector 的常見場景
- 存儲自定義類型
std::vector 不僅能存int、string等基礎類型,還能存自定義結構體/類:
cpp
struct Student {
string name;
int age;
};
int main() {
vector<Student> stu_vec;
// 用 emplace_back 直接構造 Student 對象(高效)
stu_vec.emplace_back("張三", 18);
stu_vec.emplace_back("李四", 19);
// 遍歷輸出
for (auto& stu : stu_vec) { // 用 auto 簡化類型,& 避免拷貝
cout << stu.name << " " << stu.age << endl;
}
return 0;
}
- 作為函數參數/返回值
- 傳參:建議用引用(&) 或常量引用(const &),避免拷貝整個向量(尤其是數據量大時);
- 返回值:C++11 後支持“移動語義”,直接返回向量不會有額外拷貝開銷,放心用:
cpp
// 常量引用傳參:只讀,不修改向量
void printVec(const vector<int>& vec) {
for (int num : vec) cout << num << " ";
}
// 直接返回向量(C++11+ 高效)
vector<int> getEvenNumbers(int n) {
vector<int> res;
for (int i = 0; i <= n; i += 2) {
res.push_back(i);
}
return res;
}
int main() {
vector<int> even_vec = getEvenNumbers(10); // 得到 [0,2,4,6,8,10]
printVec(even_vec);
return 0;
}
四、注意事項
不支持在中間高效插入元素:因為底層是連續內存,中間插入需要移動後續所有元素,時間複雜度 O(n),如果需要頻繁中間插入,建議用 std::list ;
越界訪問風險:用 [] 訪問時沒有越界檢查,Debug 階段可用 at() 或開啓編譯器越界檢查(如 GCC 的 -fsanitize=address );
清空元素不釋放內存: clear() 只會把 size 設為 0, capacity 不變,若想釋放內存,可藉助“臨時向量”的特性:
cpp
vector<int> vec = {1,2,3,4,5};
vec.clear(); // size=0,capacity=5
// 用臨時向量交換,釋放內存(capacity 會變成 0)
vector<int>().swap(vec);
std::vector 是 C++ 開發的“基石工具”,掌握它的基礎用法、擴容原理和實戰技巧,能解決大部分動態數組場景的需求,日常開發中優先用它,而非手動管理數組~**