一、順序容器型別
順序容器提供了元素儲存。
順序容器也提供了對元素的順序訪問,但不一定提供直接訪問特定元素的能力(除了std::array和std::vector,它們提供了基於索引的直接訪問)。
以下是C++ STL中主要的順序容器型別:
-
std::vector:
- 動態陣列,可以動態地增長和縮小。
- 提供基於索引的
快速直接訪問
。 插入和刪除
元素(尤其是在末尾之外的位置)可能涉及移動多個元素,速度會很慢
。
-
std::deque:
-
雙端佇列,支援在
兩端快速插入和刪除元素
。 -
提供基於索引的直接訪問,但提供了在兩端進行操作的成員函式。
-
內部實現可能涉及多個固定大小的陣列,因此可以高效地處理在兩端的操作。
-
C++11及以後的版本,std::deque提供了operator[],它允許你透過索引訪問元素,就像std::vector一樣。但是,與std::vector不同,std::deque的operator[]不提供對容器大小的檢查,所以如果你使用一個超出容器範圍的索引,它可能會導致未定義的行為。
-
-
std::list:
- 雙向連結串列,允許在任何位置快速插入和刪除元素。
- 不提供基於索引的直接訪問,但提供了基於迭代器的遍歷。
- 在任何位置插入和刪除元素的成本都是常數時間。
-
std::forward_list:
- 單向連結串列,類似於std::list但只提供前向迭代。
- 由於其單向性質,它通常比std::list更節省空間。
- 插入和刪除操作與std::list相似。
-
std::array:
- 固定大小的陣列,提供了基於索引的直接訪問。
- 由於其大小是固定的,因此不支援動態增長或縮小。
- 通常用於需要固定大小陣列的場景,並且希望獲得與原生陣列類似的效能。
-
std::string:
- 用於儲存字元序列的專用容器。
- 在很多方面與std::vector
類似,但針對-字串操作進行了最佳化。 - 提供了許多方便的字串處理成員函式。
-
std::queue 和 std::stack:
- 這些實際上是容器介面卡,它們使用底層容器(如std::deque或std::list)來提供佇列或棧的語義。
- 它們不是獨立的容器型別,但提供了佇列和棧的介面。
二、如何選取容器
通常情況下,使用vector是最好的選擇。
- 程式有很多小元素,且空間的額外開銷很重要,不要使用list和forward_list。
- 要求隨機訪問元素,使用vector和deque。
- 從中間插入或者刪除,使用list或forward_list。
- 頭尾位置插入和刪除,但不在中間進行操作,使用deque。
- 既要隨機訪問,又要在中間位置插入刪除,需要綜合考慮。
三、容器庫
每個容器都定義在一個標頭檔案中,檔名與型別名相同。
順序容器幾乎可以儲存任意型別的元素
vector<vector<string>> lines; //vector的vector
//lines是vector,元素的型別是string的vector。
1、容器操作:
2、迭代器
一個迭代器範圍是由一組迭代器表示的。
[begin,end) 左閉右開區間,begin指向開始的第一個元素,end指向最後的元素的下一個元素。
1、構成一個迭代器範圍的要求:
-
指向同一個容器中的元素,或者是容器最後一個元素之後的位置。
-
begin透過反覆遞增可以達到end,end不在begin之前。
-
begin == end,範圍為空
-
begin != end,範圍內至少包含一個元素
2、begin和end有多個版本:
實際上有兩個begin成員,一個是const成員,返回容器const_iterator型別。一個是非const成員,返回iterator型別。
rbegin、end、rend也是類似情況。
3、容器定義和初始化
3.1、將一個容器初始化為另一個容器的複製
- 直接複製整個容器(容器型別和元素型別都必須相等)
- 複製迭代器對指定的範圍(容器型別和元素型別可以不等,只要元素型別能相互轉化即可)
vector<const char*> vec = {"hello","hi"};
//list<string> list1(vec); //容器型別不匹配
//vector<int> vec2(vec); //元素型別不匹配
//forward_list<int> word(vec.begin(),vec.end());
//int不能轉化為string型別
//const char*可以轉換為string型別
forward_list<string> word2(vec.begin(),vec.end());
//string可以以轉化為const char*型別
vector<string> vec2 = {"h","j","k"};
list<const char*> list2(vec2.begin(),vec2.end());
還有一種用法:
//假定迭代器it表示vec中的一個元素
//複製元素,直到(但是不包括)it指向的元素
deque<string> list(vec.begin(), it);
3.2、列表初始化
list<string> list1 = {"hello", "hi"};
3.3、建構函式初始化
vector<int> vec1(10, -1); //10個int元素,都為-1
list<string> list1(10, "hello"); //初始化10個hello
forward_list<int> flist1(20); //初始化10個0
deque<string> vec2(10); //初始化10個空string
注意:僅順序容器的建構函式才接受引數大小,關聯容器不接受。
3.4、array型別
array型別不只是需要指定型別,還要指定大小。
//array<int> a1; //錯誤:引數太少
array<int,20> a2;
array進行列表初始化,初始值的數目必須等於或者小於array的大小。
array<int, 10> a1; //預設10個都為0
array<int, 10> a2 = {1,2,3,4,5,6,7,8,9,10};
//列表初始化
array<int, 10> a3 = {1}; //只有a3[0]是1,其他都為0
內建陣列型別不能複製或者賦值,但是array可以
int a[10] = {0,1,2,3,4,5,6,7,8,9};
int b[10] = a; //錯誤:內建陣列不支援複製或者賦值
array<int, 10> a1 = {0,1,2,3,4,5,6,7,8,9};
array<int, 10> a2 = a1;
//正確:陣列型別匹配即合法