閱讀導航
- 前言
- 一、vector簡介
- 1. 概念
- 2. 特點
- 二、vector的使用
- 1.vector 構造函數
- 2. vector 空間增長問題
- ?resize 和 reserve 函數
- 3. vector 增刪查改
- ?operator[] 函數
- 三、迭代器失效
- 溫馨提示
前言
前面我們講了C語言的基礎知識,也了解了一些數據結構,并且講了有關C++的命名空間的一些知識點以及關于C++的缺省參數、函數重載,引用 和 內聯函數也認識了什么是類和對象以及怎么去new一個 ‘對象’ ,也相信大家都掌握的不錯,接下來博主將會帶領大家繼續學習有關C++比較重要的知識點——STL(vector)。下面話不多說坐穩扶好咱們要開車了😍
一、vector簡介
1. 概念
std::vector
是C++標準庫中的一個容器類模板,是一種動態數組,可以存儲相同類型的元素。它提供了動態調整大小、快速隨機訪問、插入和刪除元素的操作。(vector的官方介紹鏈接)
2. 特點
-
動態調整大小:
std::vector
會自動處理內存的分配和釋放,可以根據需要在運行時動態調整容器的大小。當添加或刪除元素時,std::vector
會自動調整內部數組的大小以適應新的元素數量。 -
快速隨機訪問:
std::vector
中的元素在內存中是連續存儲的,因此可以通過下標或迭代器快速訪問任意位置的元素。具有常數時間復雜度的隨機訪問使得std::vector
非常適合需要頻繁訪問元素的場景。 -
動態增長和收縮:當需要添加更多元素時,
std::vector
會自動擴展內部數組的大小,以容納新的元素;而當需要刪除元素時,std::vector
會自動收縮內部數組的大小,以減少內存的使用。 -
插入和刪除操作:
std::vector
提供了插入和刪除元素的方法。可以在指定位置插入一個或多個元素,也可以通過下標或迭代器刪除指定位置的元素。 -
元素的訪問和遍歷:可以通過下標或迭代器訪問指定位置的元素。使用迭代器可以對
std::vector
進行遍歷,包括正向和反向遍歷。 -
其他功能:除了上述基本功能外,
std::vector
還提供了其他一些有用的功能,如獲取容器的大小、判斷容器是否為空、重新分配容器的大小、與其他容器進行交換、排序等。
注意:使用std::vector
需要包含頭文件<vector>
,命名空間為 std
。std::vector
是一個模板類,需要指定存儲的元素類型,如std::vector<int>
表示存儲int
類型的元素。
二、vector的使用
1.vector 構造函數
std::vector
提供了多個構造函數來創建和初始化向量,這些構造函數使得創建 std::vector
對象變得靈活,并提供了多種初始化向量的方式。選擇適當的構造函數取決于具體的需求和數據源。
- 默認構造函數
vector()
該構造函數創建一個空的 std::vector
對象。
- 大小和初始值構造函數
vector(size_type count, const T& value = T())
該構造函數創建一個包含 count
個元素的 std::vector
對象,并用 value
的值初始化每個元素。如果未提供 value
的值,則使用類型 T
的默認構造函數進行初始化。
- 范圍構造函數
template <class InputIt>
vector(InputIt first, InputIt last)
該構造函數使用迭代器范圍 [first, last)
中的元素創建一個 std::vector
對象。可以通過傳遞兩個迭代器來指定范圍,從而將其他容器中的元素復制到新的 std::vector
對象中。
- 拷貝構造函數
vector(const vector& other)
該構造函數創建一個與 other
相同的 std::vector
對象,復制 other
中的所有元素。
- 移動構造函數
vector(vector&& other)
該構造函數使用 other
的資源來創建一個新的 std::vector
對象,同時將 other
置為空。
- 初始化列表構造函數
vector(std::initializer_list<T> init)
該構造函數使用花括號 {}
內的元素列表初始化 std::vector
對象。可以通過傳遞元素的列表來創建和初始化向量。
下面是所有使用構造函數創建 std::vector
對象的代碼:
#include <vector>
#include <iostream>int main() {// 默認構造函數std::vector<int> numbers; // 空的向量// 大小和初始值構造函數[10, 10, 10, 10, 10]std::vector<int> numbers1(5, 10); // 范圍構造函數[1, 2, 3, 4, 5]std::vector<int> numbers2 = {1, 2, 3, 4, 5}; // 拷貝構造函數[1, 2, 3, 4, 5]std::vector<int> numbers3 = numbers2; // 移動構造函數[1, 2, 3, 4, 5]std::vector<int> numbers4 = std::move(numbers2); // numbers2 現在為空// 初始化列表構造函數[1, 2, 3, 4, 5]std::vector<int> numbers5 {1, 2, 3, 4, 5}; // 輸出向量內的元素for (const auto& num : numbers5) {std::cout << num << " ";}std::cout << std::endl;return 0;
}
2. vector 空間增長問題
在 std::vector
中,空間增長(space growth)是指在向向量添加元素時,當容量不足時,向量自動增加內部存儲空間的過程。這是為了確保向量能夠容納更多的元素,并減少頻繁的內存重新分配操作。
- 容量和大小
-
大小(size):指的是
std::vector
中當前擁有的元素個數。 -
容量(capacity):指的是
std::vector
內部存儲空間的大小,即它可以容納的元素個數。
- 空間分配器(allocator)
std::vector
使用內部的空間分配器來動態管理存儲空間。默認情況下,它使用標準全局的 operator new
和 operator delete
來分配和釋放內存。也可以提供自定義的空間分配器來滿足特定的需求。
- 容量增長策略
std::vector
使用一種策略來決定何時以及如何增加內部存儲空間的容量:
-
固定增長因子:許多實現會以固定的因子(例如2倍)來增加容量。這意味著每次增長時,容量都會以固定比例增加。這種策略通常具有較好的性能,但可能會占用更多的內存。
-
線性增長:某些實現會以固定的大小(如每次增加n個元素)增加容量。這可以提供更節省內存的方式,但在某些情況下可能導致頻繁的重新分配操作。
容量增長過程通常包括以下步驟:
-
std::vector
判斷當前的容量是否能夠容納新的元素。如果容量不足,進入空間增長階段。 -
分配新的更大的內部存儲空間。這通常涉及到申請更大的內存塊,并將已有的元素從舊的內存塊拷貝到新的內存塊中。
-
釋放舊的內部存儲空間,回收內存資源。
-
更新
std::vector
的容量信息,以反映新的內部存儲空間大小。
空間增長的頻率取決于添加元素的數量和容器的策略。過于頻繁的空間增長可能會導致性能下降,因為內存重新分配和元素拷貝都是開銷較大的操作。因此,預先分配一定容量的空間(使用 std::vector::reserve()
)可以減少空間增長的次數,提高性能。
注意:自定義類型的元素在空間增長過程中會涉及到拷貝構造函數和析構函數的調用。確保自定義類型的正確實現這些特殊函數是非常重要的,以避免潛在的內存錯誤和資源泄漏。
?resize 和 reserve 函數
在 C++ 的 std::vector
中,reserve()
和 resize()
是兩個用于管理向量容量和大小的成員函數。
- reserve(n)
reserve()
函數用于預留至少能夠容納 n
個元素的內部存儲空間,而不會改變向量的大小。這樣做是為了避免頻繁的重新分配和拷貝操作,提高性能。
調用 reserve(n)
后,如果 n
大于當前容量(capacity()
),則向量會重新分配新的內部存儲空間,容量會大于等于 n
。如果 n
小于或等于當前容量,則不會有任何變化。
void reserve(size_type n);
- resize(n, value)
resize()
函數用于改變向量的大小,并根據需要添加或刪除元素。
- 如果
n
小于當前大小(size()
),則會刪除超出n
個元素后的元素,向量的大小變為n
。 - 如果
n
大于當前大小,則會添加足夠的元素,以便向量的大小變為n
。添加的元素將使用value
進行初始化。
void resize(size_type n);
void resize(size_type n, const value_type& value);
注意:如果增加了向量的大小,新添加的元素(在當前大小和 n
之間)將通過拷貝構造函數進行初始化。如果存儲的是自定義類型的元素,確保該類型的拷貝構造函數正確實現是很重要的。
以下是 reserve()
和 resize()
的示例代碼:
#include <iostream>
#include <vector>int main() {std::vector<int> numbers;numbers.reserve(10); // 預留至少能容納 10 個元素的空間std::cout << "Capacity: " << numbers.capacity() << std::endl; // 輸出容量numbers.resize(5); // 更改大小為 5std::cout << "Size after resize: " << numbers.size() << std::endl; // 輸出大小numbers.resize(8, 42); // 更改大小為 8,并用值 42 進行初始化std::cout << "Size after resize 2: " << numbers.size() << std::endl; // 輸出大小return 0;
}
總的來說,reserve()
用于預留足夠的存儲空間以容納更多的元素,而 resize()
用于改變向量的大小并根據需要添加或刪除元素。這兩個函數可以幫助我們靈活地管理 std::vector
的容量和大小,以滿足不同的需求。
3. vector 增刪查改
當使用 C++ 的 std::vector
容器時,可以使用下面的方法來執行向向量中增加數據、刪除數據、查找數據和修改數據的操作。
- 增加數據
向 std::vector
中增加數據可以使用 push_back()
函數將元素添加到向量的末尾。
void push_back(const T& value);
使用示例:
#include <iostream>
#include <vector>int main() {std::vector<int> numbers;numbers.push_back(10);numbers.push_back(20);numbers.push_back(30);for (const auto& num : numbers) {std::cout << num << " ";}std::cout << std::endl;return 0;
}
- 刪除數據
可以使用 erase()
函數從向量中刪除數據。它接受一個迭代器參數,指示要刪除的元素位置。
iterator erase(iterator position);
使用示例:
#include <iostream>
#include <vector>int main() {std::vector<int> numbers = {10, 20, 30};numbers.erase(numbers.begin() + 1); // 刪除索引為 1 的元素for (const auto& num : numbers) {std::cout << num << " ";}std::cout << std::endl;return 0;
}
- 查找數據
可以使用 std::find()
算法函數在向量中查找特定的元素。該函數接受兩個迭代器參數,指示要搜索的范圍,以及要查找的值。它返回指向找到的元素的迭代器,如果未找到,則返回指向容器末尾的迭代器。
iterator find(iterator first, iterator last, const T& value);
使用示例:
#include <iostream>
#include <vector>
#include <algorithm>int main() {std::vector<int> numbers = {10, 20, 30};auto it = std::find(numbers.begin(), numbers.end(), 20);if (it != numbers.end()) {std::cout << "Element found at index: " << std::distance(numbers.begin(), it) << std::endl;} else {std::cout << "Element not found" << std::endl;}return 0;
}
- 修改數據
可以通過迭代器來訪問和修改 std::vector
中的元素。通過修改迭代器指向的元素來實現數據的修改。
使用示例:
#include <iostream>
#include <vector>int main() {std::vector<int> numbers = {10, 20, 30};auto it = numbers.begin();++it;*it = 25; // 修改元素值for (const auto& num : numbers) {std::cout << num << " ";}std::cout << std::endl;return 0;
}
?operator[] 函數
還可以使用 operator[]
運算符來訪問 std::vector
中的元素并進行修改。operator[] 函數的官方鏈接
operator[]
運算符允許通過索引來訪問向量中的元素,并提供了對元素的非常快速的隨機訪問。索引從 0 開始,依次增加。
使用示例:
#include <iostream>
#include <vector>int main() {std::vector<int> numbers = {10, 20, 30};std::cout << numbers[0] << std::endl; // 訪問索引為 0 的元素numbers[1] = 25; // 修改索引為 1 的元素的值for (const auto& num : numbers) {std::cout << num << " ";}std::cout << std::endl;return 0;
}
需要注意以下幾點:
-
使用
operator[]
訪問索引超出向量范圍的元素是未定義行為,因此要確保索引值在有效范圍內。 -
如果要對不存在的元素使用
operator[]
進行訪問,它將創建一個新元素并分配給該索引位置。因此,在使用operator[]
之前,通常需要確保向量具有足夠的大小。 -
operator[]
返回對索引位置的引用,因此可以通過返回的引用進行修改和賦值操作。
operator[]
運算符是一種快速訪問和修改 std::vector
中元素的便捷方式,它提供了類似于數組的索引操作。
三、迭代器失效
在 std::vector
中,迭代器失效指的是當在向量進行插入或刪除操作后,原本指向向量元素的迭代器可能會失效,即不能再安全地使用。
迭代器失效的問題存在于插入和刪除操作中,因為這些操作可能會導致向量重新分配內存,使得原來的迭代器指向的位置變得無效。下面是常見的迭代器失效情況:
- 在插入或刪除一個元素后,迭代器失效
當向向量中插入或刪除元素時,迭代器可能會失效。插入操作可能導致內部存儲重新分配,而刪除操作可能使元素位置向前移動。這會導致之前的迭代器指向的位置變得無效。
std::vector<int> numbers = {10, 20, 30};
auto it = numbers.begin();numbers.insert(it + 1, 15); // 在索引為 1 的位置插入元素// 在插入之后,迭代器 it 可能失效,不能再使用
- 使用被刪除元素的迭代器,迭代器失效
當使用被刪除元素的迭代器繼續訪問向量時,迭代器可能會失效。因為刪除元素會導致元素位置的變動,原本的迭代器指向的位置可能已經被其他元素占據。
std::vector<int> numbers = {10, 20, 30};
auto it = numbers.begin();numbers.erase(it); // 刪除首個元素// 在刪除之后,迭代器 it 變成無效的迭代器,不能再使用
為了避免迭代器失效問題,可以采取以下幾種方法:
-
使用索引替代迭代器:使用基于索引的訪問方式,而不是依賴迭代器。
-
在插入或刪除元素后重新獲取迭代器:在插入或刪除操作后,重新獲取迭代器,而不是繼續使用之前的迭代器。
std::vector<int> numbers = {10, 20, 30};
auto it = numbers.begin();numbers.insert(it + 1, 15);
it = numbers.begin(); // 插入后重新獲取迭代器numbers.erase(it);
it = numbers.begin(); // 刪除后重新獲取迭代器
- 使用算法和函數:使用標準庫提供的算法和函數,而不是手動操作迭代器。這些函數會處理好迭代器失效的情況,例如使用
std::for_each
遍歷向量。
std::vector<int> numbers = {10, 20, 30};
std::for_each(numbers.begin(), numbers.end(), [](int& num) {// 修改元素值
});
了解迭代器失效的情況,并采取適當的措施來避免迭代器失效問題,是在操作 std::vector
時需要注意的重要方面。
溫馨提示
感謝您對博主文章的關注與支持!在閱讀本篇文章的同時,我們想提醒您留下您寶貴的意見和反饋。如果您喜歡這篇文章,可以點贊、評論和分享給您的同學,這將對我提供巨大的鼓勵和支持。另外,我計劃在未來的更新中持續探討與本文相關的內容。我會為您帶來更多關于C++以及編程技術問題的深入解析、應用案例和趣味玩法等。請繼續關注博主的更新,不要錯過任何精彩內容!
再次感謝您的支持和關注。我們期待與您建立更緊密的互動,共同探索C++、算法和編程的奧秘。祝您生活愉快,排便順暢!