初識順序容器

baobaobashi發表於2024-05-27

一、順序容器型別

順序容器提供了元素儲存。

順序容器也提供了對元素的順序訪問,但不一定提供直接訪問特定元素的能力(除了std::array和std::vector,它們提供了基於索引的直接訪問)。

以下是C++ STL中主要的順序容器型別:

  1. std::vector:

    • 動態陣列,可以動態地增長和縮小。
    • 提供基於索引的快速直接訪問
    • 插入和刪除元素(尤其是在末尾之外的位置)可能涉及移動多個元素,速度會很慢
  2. std::deque:

    • 雙端佇列,支援在兩端快速插入和刪除元素

    • 提供基於索引的直接訪問,但提供了在兩端進行操作的成員函式。

    • 內部實現可能涉及多個固定大小的陣列,因此可以高效地處理在兩端的操作。

    • C++11及以後的版本,std::deque提供了operator[],它允許你透過索引訪問元素,就像std::vector一樣。但是,與std::vector不同,std::deque的operator[]不提供對容器大小的檢查,所以如果你使用一個超出容器範圍的索引,它可能會導致未定義的行為。

  3. std::list:

    • 雙向連結串列,允許在任何位置快速插入和刪除元素。
    • 不提供基於索引的直接訪問,但提供了基於迭代器的遍歷。
    • 在任何位置插入和刪除元素的成本都是常數時間。
  4. std::forward_list:

    • 單向連結串列,類似於std::list但只提供前向迭代。
    • 由於其單向性質,它通常比std::list更節省空間。
    • 插入和刪除操作與std::list相似。
  5. std::array:

    • 固定大小的陣列,提供了基於索引的直接訪問。
    • 由於其大小是固定的,因此不支援動態增長或縮小。
    • 通常用於需要固定大小陣列的場景,並且希望獲得與原生陣列類似的效能。
  6. std::string:

    • 用於儲存字元序列的專用容器。
    • 在很多方面與std::vector類似,但針對-字串操作進行了最佳化。
    • 提供了許多方便的字串處理成員函式。
  7. std::queue 和 std::stack:

    • 這些實際上是容器介面卡,它們使用底層容器(如std::deque或std::list)來提供佇列或棧的語義。
    • 它們不是獨立的容器型別,但提供了佇列和棧的介面。

二、如何選取容器

通常情況下,使用vector是最好的選擇。

  1. 程式有很多小元素,且空間的額外開銷很重要,不要使用list和forward_list。
  2. 要求隨機訪問元素,使用vector和deque。
  3. 從中間插入或者刪除,使用list或forward_list。
  4. 頭尾位置插入和刪除,但不在中間進行操作,使用deque。
  5. 既要隨機訪問,又要在中間位置插入刪除,需要綜合考慮。

三、容器庫

每個容器都定義在一個標頭檔案中,檔名與型別名相同。
順序容器幾乎可以儲存任意型別的元素

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、將一個容器初始化為另一個容器的複製

  1. 直接複製整個容器(容器型別和元素型別都必須相等)
  2. 複製迭代器對指定的範圍(容器型別和元素型別可以不等,只要元素型別能相互轉化即可)
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;
//正確:陣列型別匹配即合法

相關文章