C++ STL模板庫9-容器2-vector
文章目錄
- C++ STL模板庫9-容器2-vector
- 一、基礎概念
- 1. 類型成員(Type Members)
- 2. 模板參數
- 二、構造函數
- 1. 語法
- 2. 示例
- 三、元素訪問
- 1. 函數說明
- 2. 示例代碼
- 四、容量操作
- 1. 函數說明
- 2. 關鍵點說明
- 3. 關鍵操作解析
- 4. 操作示例
- 五、修改
- 1. 函數說明
- 2. 關鍵操作解析
- 3. 操作示例
- 4. 操作技巧
- 六、迭代器操作
- 1. 函數說明
- 2. 關鍵操作解析
- 3. 操作示例
- 4. 注意事項
- 七、高效操作(C++11+)
- 1. 函數說明
- 2. 關鍵操作解析
- 3. 常見錯誤規避
- 4. 優先選擇原則
- 八、非成員函數
- 1. 函數說明
- 2. 示例代碼
- 3. 操作對比總結
- 九、完整應用示例
- 1. 類對象的向量
- 2. 通過數組初始化向量
- 十、關鍵特性
- 十一、應用注意事項??
- 1. 完美轉發陷阱
- 2. 高性能尾部操作
- 3. 內存釋放替代方案
- 4. 函數選擇指南
- 5. 避免裸指針操作
vector
- 動態數組,也叫向量,內存連續,動態管理內存,下標訪問,尾部增刪效率高。是最常用的容器。
一、基礎概念
1. 類型成員(Type Members)
類型 | 說明 |
---|---|
value_type | 元素類型(如 int ) |
allocator_type | 分配器類型(默認 std::allocator<T> ) |
size_type | 大小類型(通常 size_t ) |
reference | 元素引用(value_type& ) |
const_reference | 常量引用(const value_type& ) |
iterator | 隨機訪問迭代器 |
const_iterator | 常量迭代器 |
2. 模板參數
-
std::vector<T>(...) v; // T:元素類型, ...:參數
-
示例:
#include <vector>vector<int> vn; vector<string> vs;// 元素的預分配和初始化 vector<int> vn (10); // 用0初始化10個元素 vector<int> vn (10, 5); // 用5初始化10個元素
二、構造函數
1. 語法
vector(); // 默認構造
vector(size_type count, T value); // 填充構造
vector(iterator first, iterator last); // 范圍構造
vector(initializer_list<T> init); // 初始化列表構造
2. 示例
#include <iostream>
#include <vector>
#include <array>int main() {// ========== 1. 默認構造 ==========std::vector<int> v1; // 創建空vector std::cout << "v1大小: " << v1.size() << "\n"; // 輸出 0// ========== 2. 填充構造 ==========std::vector<char> v2(5, 'A'); // 5個'A'std::cout << "v2內容: ";for (char c : v2) std::cout << c; // 輸出 AAAAA// ========== 3. 范圍構造 ==========std::array<int, 3> arr{10, 20, 30};std::vector<int> v3(arr.begin(), arr.end()); // 復制數組元素 std::cout << "\nv3內容: ";for (int n : v3) std::cout << n << " "; // 輸出 10 20 30 // ========== 4. 初始化列表構造 ==========std::vector<std::string> v4 = {"Apple", "Banana", "Cherry"};std::cout << "\nv4內容: ";for (auto& s : v4) std::cout << s << " "; // 輸出 Apple Banana Cherry// ========== 組合驗證 ==========std::vector<int> v5{ v3.begin(), // 復用v3的范圍構造 v3.begin() + 2 };std::cout << "\nv5內容: ";for (int n : v5) std::cout << n << " "; // 輸出 10 20 return 0;
}
-
初始化列表的等價操作
// 以下兩種寫法等價 std::vector<int> a = {1,2,3}; std::vector<int> b({1,2,3}); // 顯式構造函數
-
顯式類型轉換
// 避免整數類型混淆 std::vector<int> v1(5, 2.5); // 輸出 {2,2,2,2,2}(截斷) std::vector<double> v2{5, 2.5}; // 輸出 {5.0, 2.5}(保留精度)
三、元素訪問
1. 函數說明
函數 | 說明 |
---|---|
at(size_type pos) | 邊界檢查訪問(越界拋異常) |
operator[](size_type pos) | 無檢查直接訪問 |
front() | 首元素引用 |
back() | 末元素引用 |
data() | 底層數組指針 |
2. 示例代碼
#include <iostream>
#include <vector>
#include <stdexcept> // 異常處理int main() {std::vector<int> nums{10, 20, 30, 40, 50};// ===== 1. at() std::cout << "at(2): " << nums.at(2) << '\n'; // ? 輸出 30 // ===== 2. operator[] 無檢查訪問 ===== std::cout << "operator[3]: " << nums[3] << '\n'; // ? 輸出 40 // nums[10] = 100; // ? 未定義行為(可能崩潰或數據污染)// ===== 3. front() & back() 端點訪問 =====std::cout << "front(): " << nums.front() << '\n'; // ? 輸出 10(首元素)std::cout << "back(): " << nums.back() << '\n'; // ? 輸出 50(尾元素)// ===== 4. data() 底層指針訪問 =====int* ptr = nums.data();std::cout << "data()[1]: " << ptr[1] << '\n'; // ? 輸出 20 *ptr = 100; // 修改首元素std::cout << "front()修改后: " << nums.front() << '\n'; // 輸出 100return 0;
}
- 優先選擇原則:
- 用戶輸入索引必須用
at()
安全 - 內部循環索引訪問用
operator[]
高效 - 和C兼容用
data()
兼容
- 用戶輸入索引必須用
四、容量操作
1. 函數說明
函數 | 作用描述 | 時間復雜度 | 內存影響 |
---|---|---|---|
empty() | 判斷容器是否為空 | O(1) | 無 |
size() | 獲取當前元素數量 | O(1) | 無 |
max_size() | 系統支持的最大元素數 | O(1) | 無 |
capacity() | 當前預分配內存容量 | O(1) | 無 |
reserve(new_cap) | 預擴容至指定容量 | O(n) | 可能重新分配內存 |
shrink_to_fit() | 釋放多余內存(非強制) | O(n) | 可能縮小容量 |
2. 關鍵點說明
- 大小可增可減,引起大小變化的函數包括
resize()/push_back()/pop_back()/insert()/erase()
。 - 容量只增不減,直接引起容量變化的函數只有
reserve()
一個。 - 大小的增加可能導致容量的增加,容量的變化不會引起大小的變化。
- 通過resize()增加大小,新增部分會被初始化,但是通過reserve()增加容量,新增部分不做初始化。
- 位于容量范圍內但在大小范圍外的元素,可以通過下標或迭代器訪問,但其值不確定。
- 無論是大小還是容量,它們的變化永遠發生在向量容器的尾部。
3. 關鍵操作解析
-
基礎狀態檢測(
empty()
/size()
)std::vector<int> v; std::cout << "初始狀態: " << "empty=" << v.empty() // 輸出 true << ", size=" << v.size() // 輸出 0 << "\n";v.push_back(10); std::cout << "添加元素后: "<< "empty=" << v.empty() // 輸出 false << ", size=" << v.size() // 輸出 1 << "\n";
-
容量動態增長(
capacity()
/reserve()
)std::vector<std::string> words; // 初始容量(實現相關,通常0) std::cout << "初始容量: " << words.capacity() << "\n"; // 輸出 0 words.reserve(3); // 預分配3元素空間 std::cout << "reserve(3)后容量: " << words.capacity() << "\n"; // 輸出 3 words = {"A", "B", "C", "D"}; // 可能觸發擴容(容量可能變6) std::cout << "超容添加后容量: " << words.capacity() << "\n"; // 輸出 4
-
極限容量檢測(
max_size()
)std::vector<double> data; std::cout << "系統支持最大元素數: " << data.max_size() / 1e9 << " 億" // 示例輸出 2.30584e+09 億<< "\n(實際受限于內存和系統架構)\n";
-
內存回收操作(
shrink_to_fit()
)std::vector<char> buffer(1000); buffer.erase(buffer.begin()+100, buffer.end()); // 刪除900元素 std::cout << "刪除后: size=" << buffer.size() // 輸出 100 << ", capacity=" << buffer.capacity() // 輸出 1000 << "\n";buffer.shrink_to_fit(); // 請求釋放內存 std::cout << "shrink后: capacity=" << buffer.capacity() // 輸出 100(可能)<< "\n(實際效果依賴編譯器實現)\n";
-
size()
vscapacity()
關系
-
擴容策略優化(以GCC為例)
// GCC擴容規則:new_cap = max(2*old_cap, required_size) std::vector<int> v; v.reserve(1); // capacity=1 v.push_back(1); v.push_back(2); // 觸發擴容 → capacity=2 v.push_back(3); // 再次擴容 → capacity=4
-
shrink_to_fit()
的底層實現// 典型實現流程 if (size() < capacity()) {vector tmp(*this); // 拷貝構造新vector(按size分配)swap(tmp); // 交換數據指針 }
4. 操作示例
// 向量的大小和容量
#include <iostream>
#include <vector>
#include <algorithm>using namespace std;
void print (vector<int>& vn) {std::cout << "大小:" << vn.size () << " ";std::cout << "容量:" << vn.capacity () << std::endl;for (vector<int>::iterator it = vn.begin ();it != vn.end (); it++)cout << *it << ' ';cout << endl;
}int main (void) {vector<int> v1 (10);print (v1); //大小:10 容量:10 //0 0 0 0 0 0 0 0 0 0v1.push_back (23);print (v1); //大小:11 容量:20 //0 0 0 0 0 0 0 0 0 0 23v1.resize (5);print (v1); //大小:5 容量:20 //0 0 0 0 0v1.resize (30);print (v1); //大小:30 容量:30 //0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 for (int i = 0; i < 10; i++)//大小:20 容量:30v1.pop_back ();print (v1); //0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0v1.reserve (40); //大小:20 容量:40print (v1); //0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0v1.reserve (10); //大小:20 容量:40print (v1); //0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0v1.insert (v1.begin () + 4, 55);//大小:21 容量:40print (v1); //0 0 0 0 55 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0v1.erase (v1.begin () + 4);//大小:20 容量:40print (v1); //0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0v1.insert (v1.begin () + 4, 66);//大小:21 容量:40print (v1); //0 0 0 0 66 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0vector<int>::iterator it =find (v1.begin (), v1.end (), /*66*/88);if (it == v1.end ())cout << "沒找到!" << endl;//沒找到!elsecout << *it << endl; //66 int arr[] = {12, 56, 77, 45, 98, 100, 2};int *p = find (arr, arr + sizeof (arr) / sizeof (arr[0]), 45);cout << *p << endl; // 45return 0;
}
五、修改
1. 函數說明
函數 | 說明 | 內存影響 |
---|---|---|
clear() | 清空所有元素 | 元素銷毀,容量保留 |
push_back(const T& value) | 尾部插入元素 | 可能觸發擴容 |
pop_back() | 刪除尾部元素 | 尾部元素銷毀(容量不變) |
insert(iterator pos, const T& value) | 指定位置插入 | 插入節點后元素后移 |
erase(iterator pos) | 刪除指定元素 | 刪除節點后元素前移 |
resize(size_type count) | 調整元素數量 | 增刪默認構造元素 |
swap(vector& other) | 交換兩個容器內容 | 僅交換指針 |
assign() | 完全替換 vector 內容 | 原元素全銷毀 |
2. 關鍵操作解析
-
clear()
vsresize(0)
std::vector<int> v{1,2,3}; v.clear(); // 內存狀態: [ ] 容量=3 v.resize(0); // 效果相同(但依賴實現)
-
插入刪除的性能陷阱,頭部插入和尾部插入都會使元素向后移動
// 連續頭部插入:O(n2) 性能災難! for (int i = 0; i < 1000; ++i) v.insert(v.begin(), i); // 每次插入全體后移 // 優化方案:尾部插入后反轉 for (int i = 0; i < 1000; ++i) v.push_back(i); std::reverse(v.begin(), v.end());
-
swap
的原子性優勢current_data.swap(new_data); // 原子指針交換
-
assign
的隱蔽風險std::vector<std::string> names{"Alice", "Bob"}; std::vector<std::string> temp = names; // 深拷貝備份 names.assign(1000, "Unknown"); // 原元素"Alice","Bob"被銷毀 // 若異常拋出:原數據丟失且無恢復!
3. 操作示例
#include <iostream>
#include <vector>void print(const std::vector<int>& v, const char* msg) {std::cout << msg << ": [";for (int n : v) std::cout << n << " ";std::cout << "] 容量: " << v.capacity() << "\n\n";
}int main() {// === 1. 初始狀態 === std::vector<int> nums{10, 20, 30};print(nums, "初始狀態"); // [10 20 30] 容量:3 // === 2. push_back:尾部插入 ===nums.push_back(40); print(nums, "push_back(40)"); // [10 20 30 40] 容量:6(觸發擴容)// === 3. pop_back:尾部刪除 === nums.pop_back(); print(nums, "pop_back()"); // [10 20 30] 容量:6 // === 4. insert:指定位置插入 === nums.insert(nums.begin() + 1, 99);print(nums, "insert(pos1,99)"); // [10 99 20 30] 容量:6// === 5. erase:指定位置刪除 ===nums.erase(nums.begin() + 2);print(nums, "erase(pos2)"); // [10 99 30] 容量:6// === 6. resize:調整元素數量 === nums.resize(5); // 新增2個默認0 print(nums, "resize(5)"); // [10 99 30 0 0] 容量:6 nums.resize(2); // 刪除尾部3個print(nums, "resize(2)"); // [10 99] 容量:6 // === 7. assign:完全替換內容 ===nums.assign({7, 8, 9, 10});print(nums, "assign({7,8,9,10})"); // [7 8 9 10] 容量:6 // === 8. swap:容器交換 === std::vector<int> other{100, 200};nums.swap(other); print(nums, "swap后nums"); // [100 200] 容量:2 print(other, "swap后other"); // [7 8 9 10] 容量:6// === 9. clear:清空元素 === nums.clear(); print(nums, "clear()后"); // [] 容量:2(容量保留!)return 0;
}
4. 操作技巧
-
容量預分配規則 ,提前把容量分配好,避免在循環里使用
push_back()
時擴容造成時間開銷std::vector<int> data; data.reserve(1000); // 預分配避免多次擴容 for (int i=0; i<1000; ++i) data.push_back(i); // 無擴容開銷
-
安全刪除模式
// 刪除特定條件元素(避免迭代器失效) auto it = data.begin(); while (it != data.end()) {if (*it % 2 == 0) it = data.erase(it); // erase返回下一有效迭代器 else ++it; }
-
移動語義優化
// 在 words 尾部插入字符串變量 huge_str 使用移動語義而非拷貝 std::vector<std::string> words; std::string huge_str = get_large_string(); words.push_back(std::move(huge_str)); // 移動而非拷貝
六、迭代器操作
1. 函數說明
begin() / end(); // 正向迭代器
rbegin() / rend(); // 反向迭代器
cbegin() / cend(); // 常量正向迭代器
crbegin() / crend(); // 常量反向迭代器
2. 關鍵操作解析
-
隨機訪問:下標、迭代器
vn[1] = 40; *(vn.begin () + 1) = 40;
-
基礎正向迭代(可讀寫)
std::vector<int> nums{10, 20, 30}; // 修改第二個元素 auto it = nums.begin() + 1; *it = 200; // ? 允許修改 for (auto i = nums.begin(); i != nums.end(); ++i) std::cout << *i << " "; // 輸出:10 200 30
-
反向迭代(鏡像操作)
std::vector<char> letters{'A','B','C'}; // 逆序輸出并修改首元素 auto rit = letters.rbegin(); *rit = 'Z'; // ? 修改最后一個元素 for (auto r = letters.rbegin(); r != letters.rend(); ++r)std::cout << *r << " "; // 輸出:Z B A
-
常量反向迭代(安全遍歷)
const std::string str = "HELLO"; // 逆序輸出且無法修改 for (auto cr = str.crbegin(); cr != str.crend(); ++cr)std::cout << *cr; // 輸出:OLLEH
-
迭代器類型轉換關系
std::vector<int>::iterator // begin()返回類型 std::vector<int>::const_iterator // cbegin()返回類型 std::vector<int>::reverse_iterator // rbegin()返回類型 std::vector<int>::const_reverse_iterator // crbegin()返回類型
-
與C風格數組的互操作
int arr[] = {100, 200, 300}; // 用標準庫函數獲取迭代器 auto arr_begin = std::begin(arr); // ? 等效 &arr[0] auto arr_end = std::end(arr); // ? 等效 &arr[3]
3. 操作示例
#include <iostream>
#include <vector>using namespace std;void print (vector<int>& vn)
{vector<int>::size_type size = vn.size ();cout << "元素個數:" << size << " -> ";for (vector<int>::size_type i = 0; i < size; i++)cout << vn[i] << ' ';cout << endl;
}
void show (vector<int>& vn)
{for (vector<int>::iterator it = vn.begin (); it != vn.end (); it++)cout << *it << ' ';cout << endl;
}int main (void)
{vector<int> vn;vn.push_back (10);vn.push_back (20);vn.push_back (30);print (vn); // 元素個數:3 -> 10 20 30show (vn); // 10 20 30vn.pop_back ();print (vn); // 元素個數:2 -> 10 20vn[1] = 40;print (vn); // 元素個數:2 -> 10 40vector<int>::const_iterator it = vn.begin ();it++;// (*it)++; // it的目標不可修改,如同常量指針// const xxx *或xxx const *// 而const vector<int>::iterator it 表示迭代器本身不可修改,如同指針常量// xxx * constvector<int> v1 (10);print (v1); // 元素個數:10 -> 0 0 0 0 0 0 0 0 0 0vector<int> v2 (10, 5);print (v2); // 元素個數:10 -> 5 5 5 5 5 5 5 5 5 5v1[0] = 100;v1[9] = 200;cout << v1.front () << ' ' << v1.back() << endl;//100 200return 0;
}
4. 注意事項
-
類型推導陷阱
auto it1 = nums.begin(); // 推導為iterator(可修改) auto it2 = nums.cbegin(); // 推導為const_iterator(安全)
-
范圍循環本質
for (int n : nums) { /*...*/ } // 等價于: for (auto it = nums.begin(); it != nums.end(); ++it)
-
迭代器失效場景
std::vector<int> data{1,2,3}; auto it = data.begin(); data.push_back(4); // 可能觸發擴容 *it = 5; // ? 迭代器已失效(未定義行為)
七、高效操作(C++11+)
1. 函數說明
函數 | 說明 |
---|---|
emplace_back(Args&&... args) | 直接構造尾部元素 |
emplace(iterator pos, Args&&... args) | 直接構造指定位置元素 |
2. 關鍵操作解析
// 直接構造對象(避免拷貝)
struct Point { int x, y; };
std::vector<Point> points;
points.emplace_back(3, 4); // 直接調用構造函數
// ? 推薦使用emplace的情況
v.emplace_back("text", 100); // 構造參數簡單
v.emplace(v.begin(), 1, 2, 3); // 插入位置敏感 // ?? 不推薦使用的情況
v.emplace_back(existing_obj); // 已有對象直接push_back更清晰
// 簡單類型(int/double等)
v.push_back(42); // ? 清晰簡潔 // 多參數構造的復雜類型
v.emplace_back("ID007", 9.8, Vector3D()); // ? 避免臨時對象 // 在迭代器中間插入
v.emplace(v.begin() + 2, config); // ? 減少元素移動次數 // 對象已存在時
auto obj = HeavyObject(...);
v.push_back(std::move(obj)); // ? 明確所有權轉移
3. 常見錯誤規避
錯誤案例 | 后果 | 修正方案 |
---|---|---|
emplace_back({1,2,3}) | 編譯錯誤(初始化列表歧義) | emplace_back(std::initializer_list{1,2,3}) |
emplace(pos, arg) 后使用舊迭代器 | 迭代器失效 | 接收返回的新迭代器 it = emplace(...) |
忽略移動語義類型 | 意外拷貝操作 | 對移動類型顯式使用 std::move |
4. 優先選擇原則
// 簡單類型(int/double等)
v.push_back(42); // ? 清晰簡潔 // 多參數構造的復雜類型
v.emplace_back("ID007", 9.8, Vector3D()); // ? 避免臨時對象 // 在迭代器中間插入
v.emplace(v.begin() + 2, config); // ? 減少元素移動次數 // 對象已存在時
auto obj = HeavyObject(...);
v.push_back(std::move(obj)); // ? 明確所有權轉移
八、非成員函數
1. 函數說明
函數 | 說明 |
---|---|
operator==, !=, <, > | 容器比較 |
std::swap(vector<T>& lhs, rhs) | 特化交換 |
erase(vector<T>& c, const T& value) | 刪除匹配值(C++20) |
erase_if(vector<T>& c, Predicate pred) | 條件刪除(C++20) |
2. 示例代碼
-
容器比較運算符
std::vector a{1,2,3}, b{1,2,3}, c{9};// 等值比較(需元素數量和值相同) bool eq = (a == b); // ? true(深層次比較)// 字典序比較(常用于排序) bool lt = (c < a); // ? true(9<1)
-
特化交換函數
std::vector v1{10,20}, v2{30};// 高性能交換(無拷貝,僅交換指針) std::swap(v1, v2); // v1: [30]|v2: [10,20](容量同步交換)
-
值匹配刪除
erase()
(C++20)std::vector nums{5,2,5,8};// 刪除所有值為5的元素 std::erase(nums, 5); // 結果:[2,8](返回刪除數量)
-
條件刪除
erase_if()
(C++20)std::vector data{-3,7,-1,0};// 刪除所有負數(λ表達式) std::erase_if(data, [](int x){return x<0;}); // 結果:[7,0](支持復雜邏輯)
3. 操作對比總結
函數 | 典型場景 | 優勢 |
---|---|---|
operator== | 配置比對/結果驗證 | 深度比較容器內容 |
std::swap(vector&, vector&) | 線程安全數據交換 | 零拷貝原子操作 |
erase(vector&, value) | 批量刪除特定值 | 比循環+erase高效 |
erase_if(vector&, pred) | 復雜條件刪除(如負/奇數) | 避免手寫易出錯的迭代器邏輯 |
九、完整應用示例
1. 類對象的向量
- 如果一個類類型的對象需要被存儲在向量中,那么這個類至少應該支持無參構造,以確保為這個向量所分配的內存能夠被正確的初始化。
- 該類可能還需要支持拷貝構造函數和拷貝賦值操作符重載函數。
- 如有必要可能還需要支持“==”和“<“操作符。
// 類對象的向量
#include <iostream>
#include <vector>using namespace std;class Integer {
public:Integer (void) : m_data (0) {cout << "無參構造:" << this << endl;}Integer (int data) : m_data (data) {cout << "有參構造:" << this << endl;}~Integer (void) {cout << "析構函數:" << this << endl;}Integer (const Integer& that) :m_data (that.m_data) {cout << "拷貝構造:" << &that << " -> " <<this << endl;}Integer& operator= (const Integer& that) {cout << "拷貝賦值:" << &that << " -> " <<this << endl;if (&that != this)m_data = that.m_data;return *this;}bool operator== (const Integer& that) const {return m_data == that.m_data;}bool operator< (const Integer& that) const {return m_data > that.m_data;}int m_data;
};int main (void) {cout << "-------- 1 --------" << endl;vector<Integer> vi (5, Integer (100));//1次有參構造 5次拷貝構造 6次析構cout << "-------- 2 --------" << endl;vi.erase (vi.begin ()); //刪除節點后元素前移 //4次拷貝賦值 5次析構 cout << "-------- 3 --------" << endl;vi.insert (vi.begin (), Integer (200));//插入節點后元素后移 //1次有參構造 1次拷貝構造 4次拷貝賦值 6次析構 cout << "-------- 4 --------" << endl;vi.resize (1); //調整容器大小為1 //5 次析構 cout << "-------- 5 --------" << endl;vi.resize (5); //調整容器大小為5 //5 次析構 cout << vi.capacity() << endl; //5cout << "-------- 6 --------" << endl;vi.reserve (10); //調整容器大小為 10 //5次拷貝構造 添加5個初始化的0元素cout << "-------- X --------" << endl;vi[0] = 12; //分別調用5次 有參構造 拷貝賦值 析構函數:vi[1] = 23;vi[2] = 47;vi[3] = 33;vi[4] = 47;return 0;
}
2. 通過數組初始化向量
-
核心代碼:
vector<int> v1(&arr[0], &arr[5]);
int arr[5] = {1, 2, 3, 4, 5}; vector<int> v1 (&arr[0], &arr[5]);
-
迭代器范圍原理:
-
&arr[0] 指向首元素(包含)
-
&arr[5] 是尾后指針(不包含)
-
實際構造范圍:[arr[0], arr[5]) → 等價于 arr[0]到arr[4]
// 用容器初始化容器 #include <iostream> #include <vector> using namespace std; int main (void) {int arr[5] = {1, 2, 3, 4, 5};vector<int> v1 (&arr[0], &arr[5]);//實際構造范圍:[arr[0], arr[5]) → 等價于 arr[0]到arr[4]for (int i = 0; i < v1.size (); i++)cout << v1[i] << ' ';cout << endl; // 1 2 3 4 5vector<int> v2 (v1.begin () + 1, v1.end () - 1);for (int i = 0; i < v2.size (); i++)cout << v2[i] << ' ';cout << endl; // 2 3 4for (vector<int>::reverse_iterator it = v1.rbegin (); it != v1.rend (); it++)cout << *it << ' '; // 5 4 3 2 1cout << endl;return 0; }
-
十、關鍵特性
- 動態內存管理:自動擴容(通常按2倍擴容)
- 連續存儲:支持指針算術操作
- 異常安全:強異常保證(部分操作)
- 時間復雜度:
- 隨機訪問:O(1)
- 尾部插入/刪除:均攤O(1)
- 中間插入/刪除:O(n)
十一、應用注意事項??
1. 完美轉發陷阱
std::vector<std::unique_ptr<Device>> devices;
// 錯誤:嘗試復制 unique_ptr
// devices.emplace_back(new Device()); // 正確:顯式移動
devices.emplace_back(std::make_unique<Device>());
2. 高性能尾部操作
vector<int> data;
data.reserve(1000); // 預分配避免多次擴容
for(int i=0; i<1000; ++i)
data.emplace_back(i);// C++20 安全刪除
erase_if(data, [](int x){ return x%2==0; });
3. 內存釋放替代方案
// 強制釋放內存的可靠方法
std::vector<int> huge_data = get_data();
std::vector<int>(huge_data).swap(huge_data); // 通過臨時對象交換
4. 函數選擇指南
場景 | 推薦操作 | 理論依據 |
---|---|---|
容器狀態檢測 | empty() 替代size()==0 | 語義更清晰 |
高性能批量插入 | reserve() 預分配 | 避免多次擴容開銷 |
內存敏感型系統 | shrink_to_fit() | 減少內存占用(可能有效) |
跨平臺開發 | 避免依賴max_size() | 不同系統差異過大 |
實時系統 | 慎用動態擴容 | 防止不可預測的內存分配延遲 |
5. 避免裸指針操作
// 改用data()獲取首元素指針
vector<int> v{1,2,3};
int* p = v.data(); // 等效&v[0]但更安全
實際開發中,優先使用 emplace_back()
替代 push_back()
避免拷貝,結合 reserve()
可顯著提升性能。