順序容器(vector、list、string、deque、forward_list)及迭代器、容器適配器

文章目錄

  • 概述
  • 所有容器都支持的操作
  • 迭代器
    • 迭代器支持的操作
    • 迭代器支持的算術運算
    • 容器類型
      • size_type
      • iterator 和 const_iterator
  • 容器定義和初始化
    • 拷貝初始化
    • 順序容器獨有的構造函數(array除外)
    • array的初始化
      • 與內置數組類型的區別
    • 6種初始化方法(以vector為例)
  • 賦值和swap
    • 使用assign(僅順序容器)
    • 使用swap
      • swap僅交換容器內部的數據結構
      • swap的效率
  • 容器的大小操作
    • 成員函數
      • sizeof()
      • size和capacity
        • 為什么list或array沒有capacity成員函數
      • reserve()
      • shrink_to_fit()
    • 關系運算符
      • capacity不會影響相等性判定
  • 順序容器獨有操作
    • 添加元素
      • push_back
      • push_front
      • insert
        • 格式
        • 使用insert的返回值
      • 使用emplace操作
    • 訪問元素
      • 訪問成員函數返回的是引用
      • 下標操作和安全的隨機訪問
    • 刪除元素
    • pop_front和pop_back
    • erase
    • forward_list獨有的操作
    • resize改變容器大小
    • 容器的相關操作導致的迭代器、指針、引用失效
  • vector的空間分配問題
    • 空間分配策略
    • 在重新分配內存空間時,vector采用的策略似乎是將舊的內存空間容量翻一倍:
  • string類型獨有的操作
    • 構造string的方法
    • 從一個char的vector初始化string
    • 子字符串(substr)操作
    • 改變string內容的操作
      • 支持下標的insert和erase版本
        • 實例
        • 關于string的insert使用須知
      • 接受C風格字符數組的insert和assign版本
        • assign
        • insert
          • string和C風格字符數組
          • string與string
      • append函數和replace函數
    • string搜索操作
      • 搜索操作的返回值
      • find()
      • find_first_of() and find_first_not_of()
      • 指定搜索開始位置從而實現遍歷搜索想要的子串:
    • string的比較操作——compare()函數
    • 數值轉換
      • 報錯
  • 容器適配器
    • 概念
    • 定義
      • 適配器可以使用哪些容器是有限制的
      • 重載
    • 棧適配器
    • 隊列適配器


概述

所有順序容器都提供了快速順序訪問元素的能力。

但是在兩個方面不可兼得:

? · 向容器中間添加或刪除元素

? · 非順序訪問容器中元素

在這里插入圖片描述

鏈表的添加刪除操作很快,但是訪問一個特定元素只能遍歷整個容器。

string和vector用下標訪問特定元素是很快的,但是向中間位置做增刪操作會很復雜。由于其是一段連續內存空間組成,如果插入的數據過多,則重新申請一塊足夠大的內存空間,將原來的數據復制過來,插入新數據,之后釋放原空間

隊列是一塊塊連續內存空間拼接而成,如果插入的數據過多,則找一塊足夠大的空間插入數據,將新空間與原deque連接在一起,關于增刪和訪問的特點與string和vector類似,在中間位置增刪元素的代價很高,但是在兩端增刪元素的速度堪比鏈表的增刪速度。

array比內置數組更安全也更易于使用。

PS:如果程序只有在讀取輸入時才需要在中間位置插入元素,隨后需要隨機訪問元素,則可以考慮在輸入階段使用list,輸入完成將list中的內容拷貝到vector中。

所有容器都支持的操作

在這里插入圖片描述

為什么forward_list不支持size操作?

forward_list設計目標是達到與最好的手寫單向鏈表數據結構相當的性能。因此,其沒有size操作,因為保存或計算大小會比手寫鏈表多出額外的開銷。

在這里插入圖片描述

迭代器

迭代器支持的操作

在這里插入圖片描述

forward_list迭代器不支持自減運算符(–)

迭代器支持的算術運算

下表這些算術運算只能應用于string、vector、deque、array的迭代器。list迭代器只支持遞增、遞減、==、!=,不支持比較大小。

原因在于vector和deque將元素在內存中連續保存;而list則是將元素以鏈表方式存儲,這樣的話指針的大小關系與它們指向的元素的前后關系并不一定是吻合的,實現"<“或者”>"將會非常困難和低效。

在這里插入圖片描述

容器類型

size_type

· 一個無符號類型的值

· 足夠放下任何string對象的大小

· string類的size函數返回值的類型

iterator 和 const_iterator

擁有迭代器的標準庫類型用iterator 和const_iterator來表示迭代器所指對象的類型。

  • 后者是前者的權限縮放,能讀取但不能修改所指的元素值。
  • 如果vector對象或string對象是常量,只能使用const_iterator;不是常量則兩者都能使用。

在這里插入圖片描述

容器定義和初始化

在這里插入圖片描述

拷貝初始化

拷貝初始化有兩種方式:

  1. 拷貝整個容器。兩個容器的類型及其元素類型必須匹配
  2. 拷貝一個迭代器對[begin,end)指定的范圍。元素類型可以不同,只要能將拷貝的元素轉換為要初始化的容器的元素類型即可,容器類型也可以不同。
// 列表初始化
list<string> authors = {"zhangsan", "lisi", "wangwu"};
vector<const char*> vc = {"a", "bc", "def"};list<string> ls(authors); // 正確
deque<string> ds(authors); // error:容器類型不匹配
vector<string> vs(vc); // error:元素類型不匹配
forward_list<string> fs(vc.begin(), vc.end()); 
// 正確:元素類型可以轉換

順序容器獨有的構造函數(array除外)

接收一個容器大小和一個元素初始值,不提供元素初始值的話,標準庫會創建一個值初始化器。

在這里插入圖片描述

如果元素類型是內置類型或者是具有默認構造函數的類類型,可以只為構造函數提供一個容器大小參數。如果元素類型沒有默認構造函數,除了大小參數外,必須指定一個顯式的元素初始值。

PS:說這樣的構造函數時順序容器獨有的是因為關聯容器不支持構造函數接收大小參數。

array的初始化

array與內置數組一樣,大小是類型的一部分。定義時,要同時指定元素類型和容器大小:

array<int, 10> // 類型為保存10個int的數組
array<string> // error:未指定容器大小

這也就解釋了為什么順序容器獨有的構造函數不適合array

由于大小是array類型的一部分,而構造函數會確定容器的大小,要么隱式地,要么顯式地。而允許用戶向array構造函數傳遞大小參數地行為是多余的,而且也容易出錯。

array執行列表初始化需要注意的地方:

  1. 默認構造的array是非空的:包含了與大小一樣多的、被默認初始化的元素。
  2. 列表初始化時初始值的數目必須等于或小于array的大小,小于的話array中剩余元素會執行值初始化
  3. 如果元素類型是一個類類型,必須要有一個默認構造函數,使值初始化能夠進行

在這里插入圖片描述

與內置數組類型的區別

雖然我們不能對內置數組類型進行拷貝或者對象賦值操作,但array并無此限制:

int arr[5] = {0,1,2,3,4};
int arr1[5] = arr; //error:不允許拷貝初始化
int arr2[5];
arr2 = arr; //error:不允許賦值
array<int, 5> data = {0,1,2,3,4};
array<int, 5> copy = data; //正確:只要數組類型匹配即合法

但由于右邊運算對象的大小可能與左邊運算對象的大小不同,因此array類型不允許使用花括號包圍的值列表進行賦值。

array<int, 5> a;
a = {3,4}; // error:不允許將一個花括號列表賦予數組

array進行拷貝或者對象賦值操作要注意的地方:

  1. array要求初始值的類型必須與要創建的容器類型相同
  2. 要求元素類型和大小也都一樣,因為大小是array的一部分

6種初始化方法(以vector為例)

vector<int> ilist1; // 默認初始化,vector為空——size返回0,
//表明容器中尚未有元素;capacity返回0,意味著尚未分配存儲空間。
//這種初始化方式適合于元素個數和值未知,需要在程序運行中動態添加的情況。vector<int> ilist2(ilist); // 容器拷貝初始化,
//ilist2初始化為ilist的拷貝,ilist必須與ilist2類型相同,
//即也是int的vector類型,ilist2將具有與ilist相同的容量和元素。vector<int> ilist2_1 = ilist; // 等價方式

vector<int> ilist = {1, 2, 3.0, 4, 5, 6, 7}; // ilist初始化為列表中
//元素的拷貝,列表中的元素類型必須與ilist的元素類型相容,
//在本例中必須是與整型相容的數值類型。對于整型,會直接拷貝其值,
//對于其他類型則需進行類型轉換(如3.0轉換為3)。這種初始化方式適合
//元素數量和值預先可知的情況。vector<int> ilist_1{1, 2, 3.0, 4, 5, 6, 7}; // 等價方式vector<int> ilist3(ilist.begin()+2, ilist.end()-1); // 范圍初始化,
//ilist3初始化為兩個迭代器指定范圍中的元素的拷貝,
//范圍中的元素類型必須與ilist3的元素類型相容,在本例中ilist3被初始化為
//{3, 4, 5, 6}。注意,由于只要求范圍中元素類型與待初始化的容器的元素類型相容,
//因此,迭代器來自于不同類型的容器是可能的,例如,用一個double的list的范圍
//來初始化ilist3是可行的。另外,由于構造函數只是讀取范圍中的元素并進行拷貝,
//因此使用普通迭代器還是const迭代器來指出范圍并無區別。這種初始化方法特別
//適合于獲取一個序列的子序列。vector<int> ilist4(7); // 默認值初始化,ilist4中將包含7個元素,
//每個元素進行缺省的值初始化,對于int,也就是被賦值為0,因此ilist4被初始化為
//包含7個0。當程序運行初期元素大致數量可預知,而元素的值需動態獲取時,可采用
//這種初始化方式。vector<int> ilist5(7, 3); // 指定值初始化,ilist5被初始化為
//包含7個值為3的int。

賦值和swap

在這里插入圖片描述

使用assign(僅順序容器)

賦值運算符要求左邊和右邊的運算對象具有相同的類型。但順序容器(array除外)允許我們使用assign從一個不同但相容的類型賦值。

在這里插入圖片描述

assign第二個版本接受一個整型值和一個元素值。

在這里插入圖片描述

由于其舊元素被替換,因此傳遞給assign的迭代器不能指向調用assign的容器。

使用swap

swap僅交換容器內部的數據結構

在這里插入圖片描述

調用swap后,svec1將包含24個string元素,svec2將包含10個string。除array外,交換兩個容器內容的操作很快——元素本身并未交換,swap只是交換了兩個容器的內部數據結構。

元素本身并未交換意味著,除string外,指向容器的迭代器、引用和指針在swap操作之后都不會失效。 它們仍指向swap操作之前所指向的那些元素。 但是,在swap之后,這些元素已經屬于不同的容器了。

vector<int> a{0,1,2,3,4};
vector<int> b{5,6,7,8,9};
auto abeg = a.begin();
auto bend = b.end()-1;
cout << *abeg << endl; 
cout << *bend << endl; 
cout << &abeg << endl; 
cout << &bend << endl; 
swap(a,b);
cout << *abeg << endl; 
cout << *bend << endl; 
cout << &abeg << endl; 
cout << &bend << endl; 

在這里插入圖片描述

與其他容器不同,對一個string調用swap會導致迭代器、引用和指針失效。

swap的效率

除array外,swap不對任何元素進行拷貝、刪除或插入操作,因此可以保證在常數時間內完成。

與其他容器不同,swap兩個array會真正交換它們的元素。因此,交換兩個array所需的時間與array中元素的數目成正比。

同樣的,對于array,在swap操作之后,指針、引用和迭代器所綁定的元素位置保持不變,但元素值已經與另一個array中對應元素的值進行了交換。

	array<int, 5> a{0,1,2,3,4};array<int, 5> b{5,6,7,8,9};auto abeg = a.begin();auto bend = b.end()-1;cout << *abeg << endl;cout << *bend << endl;cout << &abeg << endl;cout << &bend << endl;swap(a,b);cout << *abeg << endl;cout << *bend << endl;cout << &abeg << endl;cout << &bend << endl;

在這里插入圖片描述

PS:在新標準庫中,容器既提供成員函數版本的swap,也提供非成員版本的swap。而早期標準庫版本只提供成員函數版本的swap。非成員版本的swap在泛型編程中是非常重要的。統一使用非成員版本的swap是一個好習慣。

容器的大小操作

成員函數

sizeof()

sizeof():容器本身的大小

例:

sizeof(vector)為3 * sizeof(void*)個字節,由指向begin,指向end,指向capacity的三個值組成

size和capacity

size():已經保存的元素的數目

capacity():當前所在內存空間最多可以保存的元素數量。

為什么list或array沒有capacity成員函數

list是鏈表,當有新元素加入時,會從內存空間中分配一個新節點保存它;當從鏈表中刪除元素時,該節點占用的內存空間會被立刻釋放。因此,一個鏈表占用的內存空間總是與它當前保存的元素所需空間相等(換句話說,capacity總是等于size)。

而array是固定大小數組,內存一次性分配,大小不變,不會變化。

因此它們均不需要capacity。

reserve()

reserve()并不改變容器中元素的數量,它僅影響vector預先分配多大的內存空間。

只有當需要的內存空間大于當前容量時,reserve才會改變vector的容量,至少分配與新需求一樣大的空間(可能更大)。

新需求小于或等于當前容量的話,reserve什么也不做。即使小于也不會退回內存空間(退回是shrink_to_fit做的)。因此,在調用reserve之后,capacity將會大于或等于傳遞給reserve的參數。

shrink_to_fit()

此函數指出我們不再需要任何多余的空間,但具體的實現可以忽略此請求。即,調用此函數也不能保證一定退回內存空間。

關系運算符

  1. 每個容器類型都支持相等運算符(==和!=)
  2. 除了無序關聯容器外的所有容器都支持關系運算符(>、>=、<、<=)
  3. 關系運算符左右兩邊的運算對象必須容器類型相同,且元素類型相同
  4. 兩個容器大小相同元素相等則容器相等
  5. 兩個容器大小不同,但小的是大容器的前綴子序列那么較大容器大
  6. 兩個容器都不是另一個容器的前綴子序列則比較結果取決于第一個不相等的元素的比較結果
  7. 只有當其元素類型也定義了相應的比較運算符時,我們才可以使用關系運算符來比較兩個容器

capacity不會影響相等性判定

在這里插入圖片描述

順序容器獨有操作

在這里插入圖片描述

添加元素

push_back

在這里插入圖片描述

對push_back的調用在container尾部創建了一個新的元素,將container的size增大了1。該元素的值為word的一個拷貝。container的類型可以是list、vector或deque。

由于string是一個字符容器,我們也可以用push_back在string末尾添加字符:
在這里插入圖片描述

push_front

將元素插入到容器頭部,list、forward_list和deque容器支持。

insert

格式

接受一個迭代器作為第一個參數,簡單理解為,insert(iterator, )

  1. 可以后跟一個值 insert(iterator, “hello”)
  2. 可以后跟值初始化 insert(iterator, 10 , 'dksla")
  3. 可以后跟列表初始化 insert(iterator, {“1”, “2”, “3”})
  4. 可以后跟范圍初始化 insert(iterator, iterator1, iterator2 )

如果選擇第四種方式傳遞給insert一對迭代器,它們不能指向添加元素的目標容器。

使用insert的返回值

通過使用insert的返回值,可以在容器中一個特定位置反復插入元素:

在這里插入圖片描述

理解這個循環是如何工作的非常重要,特別是理解這個循環為什么等價于調用push_front尤為重要。

在循環之前,我們將iter初始化為lst.begin()。第一次調用insert會將我們剛剛讀入的string插入到iter所指向的元素之前的位置。insert返回的迭代器恰好指向這個新元素。我們將此迭代器賦予iter并重復循環,讀取下一個單詞。只要繼續有單詞讀入,每步while循環就會將一個新元素插入到iter之前,并將iter改變為新加入元素的位置。此元素為(新的)首元素。因此,每步循環將一個新元素插入到list首元素之前的位置。

使用emplace操作

當調用push或insert成員函數時,我們將元素類型的對象傳遞給它們,這些對象被拷貝到容器中。

而當我們調用一個emplace成員函數時,則是將參數傳遞給元素類型的構造函數。emplace成員使用這些參數在容器管理的內存空間中直接構造元素。

例如,假定c保存Sales_data元素:

在這里插入圖片描述

其中對emplace_back的調用和第二個push_back調用都會創建新的Sales_data對象

在調用emplace_back時,會在容器管理的內存空間中直接創建對象。

而調用push_back則會創建一個局部臨時對象,并將其壓入容器中。

emplace函數在容器中直接構造元素。傳遞給emplace函數的參數根據元素類型而變化,參數必須與元素類型的構造函數相匹配:

(iter指向c中一個元素,其中保存了Sales_data元素)

在這里插入圖片描述

訪問元素

在這里插入圖片描述

關于front和back的用法實例:

在這里插入圖片描述
此程序用兩種不同方式來獲取c中的首元素和尾元素的引用。直接的方法是調用front和back。而間接的方法是通過解引用begin返回的迭代器來獲得首元素的引用,以及通過遞減然后解引用end返回的迭代器來獲得尾元素的引用。

這個程序有兩點值得注意:迭代器end指向的是容器尾元素之后的(不存在的)元素。為了獲取尾元素,必須首先遞減此迭代器。另一個重要之處是,在調用front和back(或解引用begin和end返回的迭代器)之前,要確保c非空。如果容器為空,if中操作的行為將是未定義的。

訪問成員函數返回的是引用

在容器中訪問元素的成員函數(即,front、back、下標和at)返回的都是引用。 如果容器是一個const對象,則返回值是const的引用。如果容器不是const的,則返回值是普通引用,我們可以用來改變元素的值:

在這里插入圖片描述

與以前一樣,如果我們使用auto變量來保存這些函數的返回值,并且希望使用此變量來改變元素的值,必須記得將變量定義為引用類型。

下標操作和安全的隨機訪問

提供快速隨機訪問的容器(string、vector、deque和array)也都提供下標運算符。下標運算符接受一個下標參數,返回容器中該位置的元素的引用。給定下標必須“在范圍內”(即,大于等于0,且小于容器的大小)。

保證下標有效是程序員的責任,下標運算符并不檢查下標是否在合法范圍內。使用越界的下標是一種嚴重的程序設計錯誤,而且編譯器并不檢查這種錯誤。

如果我們希望確保下標是合法的,可以使用at成員函數。at成員函數類似下標運算符,但如果下標越界,at會拋出一個out_of_range異常:

在這里插入圖片描述

刪除元素

在這里插入圖片描述

pop_front和pop_back

這些操作返回void,若需要彈出元素值,則必須在執行彈出前保存它。

erase

成員函數erase從容器中指定位置刪除元素。

  1. 可以刪除由迭代器指定的單個元素
  2. 也可以刪除由一對迭代器指定的范圍內的所有元素

兩種形式erase都返回指向刪除的最后一個元素之后(insert返回第一個新增元素之前)位置的迭代器

第二種方式的例子:

elem = slist.erase(elem1, elem2); // 調用后,elem == elem2 

迭代器elem1指向我們要刪除的第一個元素,elem2指向我們要刪除的最后一個元素之后的位置。當兩個迭代器相等時,什么也不會發生,容器保持不變。

因為上述規則,因此可以使用begin和end獲得的迭代器作為參數調用erase,以起到clear的作用:

slist.clear(); // 刪除容器中所有元素
slist.erase(slist.begin(), slist.end()); // 等價調用

forward_list獨有的操作

在這里插入圖片描述

當刪除或添加一個元素,被操作元素之前(elem3)的元素(elem2)的后繼會發生改變。因此從邏輯上講,需要訪問被操作元素的前驅,以改變前驅的鏈接。但是forward_list是單向鏈表。在一個單向鏈表中,沒有簡單的方法來獲取一個元素的前驅。因此在forward_list中增刪操作是通過傳入被操作元素(elem3)之前的迭代器(elem2)來完成的。返回的迭代器是最后一個新元素的迭代器(添加操作)或者被刪元素之后的迭代器(刪除操作)。

在這里插入圖片描述

resize改變容器大小

在這里插入圖片描述

實例:

在這里插入圖片描述

同樣的,如果resize向一個保存類類型元素的容器添加新元素,則我們必須提供初始值(即必須有兩個參數),或者元素類型必須提供一個默認構造函數(接收單個參數的resize版本)。

容器的相關操作導致的迭代器、指針、引用失效

在向容器添加元素后:

· 如果容器是vector或string,且存儲空間被重新分配,則指向容器的迭代器、指針和引用都會失效。如果存儲空間未重新分配,指向插入位置之前的元素的迭代器、指針和引用仍有效,但指向插入位置之后元素的迭代器、指針和引用將會失效。

· 對于deque,插入到除首尾位置之外的任何位置都會導致迭代器、指針和引用失效。 如果在首尾位置添加元素,迭代器會失效,但指向存在的元素的引用和指針不會失效。

· 對于list和forward_list,指向容器的迭代器(包括尾后迭代器和首前迭代器)、指針和引用仍有效。

當我們從一個容器中刪除元素后,指向被刪除元素的迭代器、指針和引用會失效, 這應該不會令人驚訝。畢竟,這些元素都已經被銷毀了。當我們刪除一個元素后:

· 對于list和forward_list,指向容器其他位置的迭代器(包括尾后迭代器和首前迭代器)、引用和指針仍有效。

· 對于deque,如果在首尾之外的任何位置刪除元素,那么指向被刪除元素外其他元素的迭代器、引用或指針也會失效。如果是刪除deque的尾元素,尾后迭代器也會失效,但其他迭代器、引用和指針不受影響;如果是刪除首元素, 這些也不會受影響。

· 對于vector和string,指向被刪元素之前元素的迭代器、引用和指針仍有效。 注意:當我們刪除元素時,尾后迭代器總是會失效。

vector的空間分配問題

空間分配策略

我們都知道vector將元素連續存儲,且容器大小是可以變化的。那么在內存中,如果vector存儲地址的空間已經飽和,沒辦法容納新的元素,此時應該怎么處理呢?

簡單地將其添加到內存中的其他位置嗎?

顯然這樣是不行的,vector不同于鏈表結構,可沒有指向下一個節點的指針。

正確答案是容器必須分配新的內存空間來保存已有元素和新元素:

  1. 將已有元素從舊位置移動到新空間中
  2. 然后添加新元素
  3. 釋放舊存儲空間

這樣操作同時也有一個隱患:如果新分配的空間只能新添加一個元素就達到飽和,豈不是說我們每添加一個新元素,vector就要執行一次這樣的內存分配和釋放操作?這樣的話性能會慢到不可接受。

為了避免上述代價,當不得不獲取新的內存空間時,vector和string的實現通常會分配比新的空間需求更大的內存空間。容器預留這些空間作為備用,可用來保存更多的新元素。以此來減少容器空間重新分配次數。

上述分配策略實際性能也表現得足夠好——雖然vector每次重新分配內存空間時都要移動所有元素,但其擴張操作通常比list和deque還要快。

雖然分配策略的選擇有很多,但是都應遵循一個原則:確保用push_back向vector添加元素的操作有高效率。從時間上說,就是通過在一個初始為空的vector上調用n次push_back來創建一個n個元素的vector,所花費時間不能超過n的常數倍。

在重新分配內存空間時,vector采用的策略似乎是將舊的內存空間容量翻一倍:

在這里插入圖片描述

只有在執行insert時size與capacity相等(飽和狀態無法添加新元素)、或者調用resize/reserve時給定的大小超過當前capacity,vector才可能重新分配空間。

string類型獨有的操作

構造string的方法

在這里插入圖片描述

幾個實例:

在這里插入圖片描述

當用一個c風格字符串初始化string時,我們可以提供一個形如上例:數組名(cp)或者數組名+下標(cp+6)或者(cp,6)形式的的開始位置和一個計數值。

  1. 通常當我們從一個const char*創建string時,指針指向的數組必須以空字符結尾,拷貝操作遇到空字符時停止。

  2. 如果我們還傳遞給構造函數一個計數值,數組就不必以空字符結尾。

  3. 如果我們未傳遞計數值且數組也未以空字符結尾,或者給定計數值大于數組大小,則構造函數的行為是未定義的。

當從一個string拷貝字符時,我們可以提供一個可選的開始位置(僅數組下標,不可以s1+6 的形式)和一個計數值。

  1. 開始位置必須小于或等于給定的string的大小。如果位置大于size,則構造函數拋出一個out_of_range異常。
  2. 如果我們傳遞了一個計數值,則從給定位置開始拷貝這么多個字符。不管我們要求拷貝多少個字符,標準庫最多拷貝到string結尾,不會更多。

從一個char的vector初始化string

兩種方法:

  1. 使用迭代器(下例中的s3)
  2. 使用data成員函數(下例中的s4),將data返回值作為string的構造函數的第一個參數,將vector的size返回值作為第二個參數,即可獲取vector中的數據,將其看作一個字符數組來初始化string。

在這里插入圖片描述

關于data成員函數:

vector提供了data成員函數,其返回類型為const_pointer,返回其內存空間的首地址 (但是從上圖中可以看出vector的data成員函數返回的是容器中的元素,并且data返回的地址和begin迭代器返回的地址也不一樣)。

子字符串(substr)操作

在這里插入圖片描述
在這里插入圖片描述

如果開始位置加上計數值大于string的大小(s4),則substr會調整計數值,只拷貝到string的末尾。

改變string內容的操作

在這里插入圖片描述
在這里插入圖片描述

assign和append函數無須指定要替換string中哪個部分:assign總是替換string中的所有內容,append總是將新字符追加到string末尾。

replace函數提供了兩種指定刪除元素范圍的方式。可以通過一個位置和一個長度來指定范圍,也可以通過一個迭代器范圍來指定。

insert函數允許我們用兩種方式指定插入點:用一個下標或一個迭代器。在兩種情況下,新元素都會插入到給定下標(或迭代器)之前的位置。

可以用好幾種方式來指定要添加到string中的字符。新字符可以來自于另一個string,來自于一個字符指針(指向的字符數組),來自于一個花括號包圍的字符列表,或者是一個字符和一個計數值。當字符來自于一個string或一個字符指針時,我們可以傳遞一個額外的參數來控制是拷貝部分還是全部字符。

支持下標的insert和erase版本

實例

在這里插入圖片描述

關于string的insert使用須知

首先需要明確的一點是insert是不支持string迭代器與常量字符串兩個參數完成insert操作的:

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-cec9yKjv-1617791396682)(C:\Users\Jormungand\AppData\Roaming\Typora\typora-user-images\image-20210404134226614.png)]

但是支持其他包含string的容器(例如vector)的迭代器與常量字符串兩個參數完成insert操作:

在這里插入圖片描述

因此string有了支持下標的insert版本,這樣就能夠用兩個參數實現insert:

在這里插入圖片描述

如果實在想要使用string的迭代器和一個字符常量進行insert,那么可以將要添加的常量字符串賦給string變量,使用string變量的的迭代器作為第二個和第三個參數,也就是用三個參數來實現insert功能:

在這里插入圖片描述

接受C風格字符數組的insert和assign版本

assign

在這里插入圖片描述

通過assign替換s的內容。我們賦予s的是從c所指向的地址開始的四個字符。

計數值(第二個參數)必須小于或等于c指向的數組中的字符數(不包括結尾的空字符)。如果大于則會出現未定義的行為:

在這里插入圖片描述

insert

string和C風格字符數組

string為空串:

在這里插入圖片描述

將c[5]開始到結尾空字符之前的字符拷貝到s[0]之前的位置。因為string是空串,因此拷貝到s[size()]之前(非空串的話也就是從尾部添加元素)也是同樣的效果:

在這里插入圖片描述

string非空串:

在這里插入圖片描述
在這里插入圖片描述

指定長度:

在這里插入圖片描述

但不支持這種格式:

在這里插入圖片描述

如想使用上述格式,可以參照下面的方法?:

值得一提的是,C風格字符數組的指針亦可起到容器中迭代器的作用:

在這里插入圖片描述

string與string

也可以指定將來自其他string或子字符串的字符插入到當前string中,或者賦予當前string:

在這里插入圖片描述

也可以指定長度:

在這里插入圖片描述

子串:

在這里插入圖片描述

append函數和replace函數

append用于在string末尾進行插入字符串操作,push_back僅能插入字符

replace是調用erase和insert的一種簡寫形式(替換一部分字符其實也就是先刪除再添加的操作),其作用是將指定長度的字串,替換為新的字串,新的字串長度不需要和指定長度相同。

string搜索操作

在這里插入圖片描述

搜索操作的返回值

string類的搜索操作都返回一個string::size_type值,表示匹配發生位置的下標。如果搜索失敗,則返回一個名為string::npos的static成員。標準庫將npos定義為一個const string::size_type類型,并初始化為值-1.由于npos是一個unsigned類型,此初始值意味著npos等于任何string最大的可能大小。

find()

找到則返回第一個匹配位置的下標,否則返回npos.

搜索(以及其他string操作)是區分大小寫的:

在這里插入圖片描述

find_first_of() and find_first_not_of()

  string name("zhang2021san42");string birthday("2021li4si2");string numbers("0123456789");string::size_type pos1 = name.find_first_of(numbers);// name中第一個數字的下標,cout << pos1 << endl;unsigned pos2 = birthday.find_first_not_of(numbers);// birthday中第一個非數字字符的下標cout << pos2 << endl;auto pos3 = name.find_last_of(numbers);// 在name中查找numbers中任何一個字符最后一次出現的位置cout << pos3 << endl;

在這里插入圖片描述

指定搜索開始位置從而實現遍歷搜索想要的子串:

  string::size_type pos1 = 0;while ((pos1 = name.find_first_of(numbers,pos1)) != string::npos) {// 循環搜索name中的子字符串numbers的出現下標cout << pos1 << ends << name[pos1] << endl;pos1++; // 忽略了遞增則會死循環}

string的比較操作——compare()函數

在這里插入圖片描述

類似strcmp,根據s是等于、大于還是小于參數指定的字符串,s.compare返回0、正數或負數。

數值轉換

新標準引入了多個函數,可以是心啊數值數據與標準庫string之間的轉換:

在這里插入圖片描述

要轉換為數值的string中第一個非空白字符必須是數值中可能出現的字符:

在這里插入圖片描述

name中首先出現的是“z”,因此會發生錯誤。

在這里插入圖片描述

可以看到,由find_first_of()獲得name中第一個可能是數值一部分的字符的下標。返回給substr(),substr截取從該下標到末尾的字符,將其返回給stod(),stod()讀取此參數,處理其中的字符,直到遇到不可能是數值的一部分的字符就停止。然后將這個范圍內的字符串表示形式轉換為對應的雙精度浮點值。

提取字符串中全部的數值字符然后進行數值轉換:

感覺上一個例子只能提取一部分字符有遺憾,自己嘗試著寫了提取全部數值字符的代碼,可能不夠簡潔,有待優化:

string name("zhang2021.5san4.2");string numbers("+-.0123456789");double d; // string要轉換為doubleunsigned pos = 0; // 從下標0開始搜索數值字符string over; // 存儲數值字符的stringwhile ((pos = name.find_first_of(numbers, pos)) != string::npos) {string s; // 存儲每一次循環搜出來的數值字符s = name.substr(pos); // 將數值字符起始位置到整體末尾的字符生成子串auto pos1 = s.find_first_not_of(numbers);// 從非數值字符位置結束if(pos1 != string::npos){over.append(name.substr(pos, pos1));pos += pos1;}else{ // 可能會有以數值字符結束的字符// 因此僅以上面的非數值字符結束是不夠的// 還需要以最后出現的數值字符作為結尾auto pos2 = s.find_last_of(numbers)+1;over.append(name.substr(pos, pos2));pos += pos2;}cout << over << endl; // 調試的一部分//用來觀察每次循環結束的存儲數值字符的string的值}d = stod(over); // 將存儲數值字符的string轉化為doublecout << d << endl;

運行結果:

在這里插入圖片描述

格式要求:

  1. string參數中第一個非空白符必須是符號(+ 或 -)或數字。
  2. 它可以以0x或0X開頭來表示十六進制數。
  3. 對那些將字符串轉換為浮點值的函數,string參數也可以以小數點(.)開頭,并可以包含e或E來表示指數部分。
  4. 對于那些將字符串轉換為整型值的函數,根據基數不同, string參數可以包含字母字符,對應大于數字9的數。

將string轉換為16進制數:

在這里插入圖片描述

報錯

· 如果string不能轉換為一個數值,這些函數拋出一個invalid_argument異常。

· 如果轉換得到的數值無法用任何類型來表示,則拋出一個out_of_range異常。

容器適配器

概念

標準庫定義了三個順序容器適配器:stack、queue和priority_queue。容器、迭代器喝函數都有適配器。一個適配器是一種機制,能使某種事物的行為看起來像另外一種事物一樣。

在這里插入圖片描述

定義

每個適配器都定義了兩個構造函數:

  1. 默認構造函數:創建一個空對象
  2. 拷貝構造函數:接受一個容器、拷貝該容器來初始化適配器

默認情況下:stack和queue是基于deque實現的,priority_queue是在vector上實現的。

適配器可以使用哪些容器是有限制的

  1. 所有適配器都要求容器具有添加和刪除元素的能力,因此適配器不能構造在array上。
  2. 所有適配器都要求容器具有訪問尾元素的能力,因此適配器不能構造在forward_list上。
  3. stack只要求push_back、pop_back和back操作,因此可以使用除array和forward_list之外的任何容器類型來構造stack。
  4. queue適配器要求back、push_back、front和push_front,因此它可以構造于list或deque之上,但不能基于vector構造(于vector而言,對首元素進行操作(front/push_front)會移動后面所有元素,效率較差)。
  5. priority_queue除了front、push_back和pop_back操作之外還要求隨機訪問能力,因此它可以構造于vector或deque之上,但不能基于list構造(list只能從頭節點開始進行遍歷,無法隨機訪問)。

重載

可以在創建一個適配器時將一個命名的順序容器作為第二個類型參數,來重載默認容器類型:

  vector<string> svec;stack<string, vector<string>> str_stk;stack<string, vector<string>> str_stk2(svec);

棧適配器

stack類型定義在stack頭文件中。

在這里插入圖片描述

每個容器適配器都基于底層容器類型的操作定義了自己的特殊操作。我們只可以使用適配器操作,而不能使用底層容器類型的操作。例如:

s.push(item);

雖然stack是基于deque實現的,但我們不能在一個stack上直接使用deque操作——調用push_back,而必須使用stack自己的操作——push。

隊列適配器

queue和priority_queue適配器定義在queue頭文件中。

priority_queue的內部數據結構為

在這里插入圖片描述

PS:上表中q.pop()會刪除元素,表中表述有誤:

在這里插入圖片描述

標準庫queue使用一種先進先出(first-in,first-out,FIFO)的存儲和訪問策略。進入隊列的對象被放置到隊尾,而離開隊列的對象則從隊首刪除。

priority_queue允許我們為隊列中的元素建立優先級。新加入的元素會排在所有優先級比它低的已有元素之前。

對于基本數據類型(int,char,double),priority_queue的排序是默認是數值越大越優先。

#默認大根堆 
priority_queue<int> que;
#greater是小根堆,注意greater<int>>之間的空格
priority_queue<int, vector<int>, greater<int> > que; 
#less是大根堆,注意less<int>>之間的空格
priority_queue<int, vector<int>, less<int> > que;

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/443819.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/443819.shtml
英文地址,請注明出處:http://en.pswp.cn/news/443819.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

jQuery實現表格隔行換顏色:

jQuery實現表格各行換顏色&#xff1a; 截圖如下&#xff1a; 代碼如下&#xff1a; <span style"font-family:Microsoft YaHei;font-size:14px;"><% page language"java" import"java.util.*" pageEncoding"UTF-8"%> &…

用stack處理中綴表達式【+、-、*、/、()】

文章目錄題目描述思路使用getline()存儲輸入的字符串邊讀取邊壓棧完整代碼題目描述 使用stack處理括號化的表達式。當你看到一個左括號&#xff0c;將其記錄下來。當你在一個左括號之后看到一個右括號&#xff0c;從stack中pop對象&#xff0c;直至遇到左括號&#xff0c;將左括…

原地置換法尋找數組中重復的數

文章目錄題目描述代碼實現題目描述 在一個長度為 n 的數組 nums 里的所有數字都在 0&#xff5e;n-1 的范圍內。數組中某些數字是重復的&#xff0c;但不知道有幾個數字重復了&#xff0c;也不知道每個數字重復了幾次。請找出數組中任意一個重復的數字。 輸入&#xff1a; [2,…

二維數組的查找

文章目錄題目描述思路注意代碼題目描述 在一個 n * m 的二維數組中&#xff0c;每一行都按照從左到右遞增的順序排序&#xff0c;每一列都按照從上到下遞增的順序排序。請完成一個高效的函數&#xff0c;輸入這樣的一個二維數組和一個整數&#xff0c;判斷數組中是否含有該整數…

雙指針

文章目錄題目描述思路注意代碼實現題目描述 請實現一個函數&#xff0c;把字符串 s 中的每個空格替換成"%20"。 示例 1&#xff1a; 輸入&#xff1a;s “We are happy.” 輸出&#xff1a;“We%20are%20happy.” 限制&#xff1a; 0 < s 的長度 < 10000 思…

Springmvc,Spring MVC文件上傳

Springmvc文件上傳&#xff1a; 1.代碼截圖如下&#xff1a; 2.UploadController.java: package cn.csdn.controller;import java.io.File;import javax.servlet.http.HttpServletRequest;import org.springframework.stereotype.Controller; import org.springframework.ui.…

倒序輸出鏈表

文章目錄題目描述思路遞歸法棧題目描述 輸入一個鏈表的頭節點&#xff0c;從尾到頭反過來返回每個節點的值&#xff08;用數組返回&#xff09;。 示例 1&#xff1a; 輸入&#xff1a;head [1,3,2] 輸出&#xff1a;[2,3,1] 限制&#xff1a; 0 < 鏈表長度 < 10000 思…

插入迭代器、流迭代器、反向迭代器、移動迭代器

文章目錄前言插入迭代器inserterfront_inserterback_inserteriostream迭代器istream_iterator 讀取輸入流istream_iterator允許使用懶惰求值ostream_iterator操作反向迭代器reverse_iterator的base成員函數前言 除了為每個容器定義的迭代器之外&#xff0c;標準庫在頭文件iter…

泛型算法(lambda表達式、function類模板、bind函數適配器、迭代器類別、鏈表數據結構獨有的算法)

文章目錄概念find()函數迭代器令算法不依賴于容器但算法依賴于元素類型的操作算法永遠不會執行容器的操作只讀算法accumulate()函數從兩個序列中讀取元素&#xff08;equal函數為例&#xff09;迭代器作為參數形成兩個序列equal()寫容器元素的算法概念fill()fill_n()插入迭代器…

jsp,div 限制字數,超出部分用省略號代替

1.我是用struts2標簽做的&#xff1a;如下&#xff1a; <% page language"java" import"java.util.*" pageEncoding"UTF-8"%> <% taglib prefix"s" uri"/struts-tags"%> <%String path request.getContext…

C++之關聯容器

文章目錄概述及類型mapsetpair類型概念初始化默認初始化提供初始化器允許的操作可以創建一個pair類的函數可以作為容器的類型關聯容器迭代器概念map的迭代器set的迭代器是const的初始化map and setmultimap and multiset關聯容器的操作額外的類型別名關聯容器和算法刪除元素添加…

動態內存、智能指針(shared_ptr、unique_ptr、weak_ptr)、動態數組

文章目錄三種對象的分類三種內存的區別動態內存概念智能指針允許的操作智能指針的使用規范new概念內存耗盡/定位new初始化默認初始化直接初始化值初始化delete概念手動釋放動態對象空懸指針shared_ptr類格式獨有的操作make_shared函數shared_ptr的計數器通過new用普通指針初始化…

動態數組的簡單應用

文章目錄連接兩個字符串字面常量題目注意代碼輸出結果處理輸入的變長字符串題目注意代碼連接兩個字符串字面常量 題目 連接兩個字符串字面常量&#xff0c;將結果保存在一個動態分配的char數組中。重寫&#xff0c;連接兩個標準庫string對象。 注意 使用頭文件cstring的str…

二分查找算法實現

文章目錄思路代碼以二分區間作為while判定條件以給定值作為while判定條件主函數思路 while判定條件的選擇&#xff0c;注意最外層的return的內容就好。下面提供了兩個代碼版本。計算 mid 時需要防止溢出&#xff08;對應類型【如本例中的int】可能存不下&#xff09;&#xff…

Windows下Spring3.x計劃任務實現定時備份MySql數據庫

今天在空閑之余查了一下關于MySql數據庫備份的方案&#xff0c;最后結合自己的項目情況寫了一個關于Spring計劃任務的例子&#xff0c;目前我這個版本是在Windwos下測試成功&#xff0c;希望對大家有所幫助&#xff0c;不足之處還請大家多多包含&#xff0c;有什么建議盡管提出…

根據中序、前序遍歷重建二叉樹

文章目錄題目遞歸思路細節易錯代碼復雜度分析迭代思路細節易錯代碼復雜度分析題目 輸入某二叉樹的前序遍歷和中序遍歷的結果&#xff0c;請重建該二叉樹。假設輸入的前序遍歷和中序遍歷的結果中都不含重復的數字。 例如&#xff0c;給出 前序遍歷 preorder [3,9,20,15,7] 中…

深搜+剪枝

文章目錄題目思路注意代碼復雜度分析題目 給定一個 m x n 二維字符網格 board 和一個字符串單詞 word 。如果 word 存在于網格中&#xff0c;返回 true &#xff1b;否則&#xff0c;返回 false 。 單詞必須按照字母順序&#xff0c;通過相鄰的單元格內的字母構成&#xff0c…

搜索+回溯問題(DFS\BFS詳解)

文章目錄題目思路DFS思路代碼復雜度分析BFS思路代碼復雜度分析題目 地上有一個m行n列的方格&#xff0c;從坐標 [0,0] 到坐標 [m-1,n-1] 。一個機器人從坐標 [0, 0] 的格子開始移動&#xff0c;它每次可以向左、右、上、下移動一格&#xff08;不能移動到方格外&#xff09;&am…

剪繩子(動規、數論、貪心)

文章目錄題目數論思路代碼復雜度分析動規一思路代碼動規二思路代碼對最終結果取模1e97思路代碼題目 給你一根長度為 n 的繩子&#xff0c;請把繩子剪成整數長度的 m 段&#xff08;m、n都是整數&#xff0c;n>1并且m>1&#xff09;&#xff0c;每段繩子的長度記為 k[0],…

快速冪實現pow函數(從二分和二進制兩種角度理解快速冪)

文章目錄迭代實現快速冪思路int的取值范圍快速冪從二進制的角度來理解從二分法的角度來理解代碼復雜度分析進階——超級次方思路倒序快速冪正序快速冪代碼復雜度分析迭代實現快速冪 實現 pow(x, n) &#xff0c;即計算 x 的 n 次冪函數&#xff08;即&#xff0c;xn&#xff0…