C++STL之string類的使用
1.STL簡介
(1)什么是STL
STL(standard template libaray-標(biāo)準(zhǔn)模板庫):是C++標(biāo)準(zhǔn)庫的重要組成部分,不僅是一個可復(fù)用的組件庫,而且是一個包羅數(shù)據(jù)結(jié)構(gòu)與算法的軟件框架。
可以理解為就是一個類似stdio.h的庫,包含了這個庫,里面的語法或者函數(shù)可以直接使用。
(2)STL的版本
最原始的版本是由Alexander Stepanov和Meng Lee開發(fā)的,并且他們要求對其進(jìn)行開源處理。之后又經(jīng)歷了PJ.版本,RW版本,SGI版本等,目前SGI版本最應(yīng)用最廣泛的版本,我們后面學(xué)習(xí)STL要閱讀部分源代碼,主要參考這一版本。
總結(jié)一下目前開源與閉源的操作系統(tǒng)或者軟件:
開源:STL,linux,git,Vue
閉源:IOS,Windows
(3)如何學(xué)習(xí)STL
第一境界:熟練使用STL。
第二境界:了解STL的源代碼,即了解底層。
第三境界:會自己擴(kuò)充STL庫。
廢話不多說,正式進(jìn)入主題:
(4)STL的六大組件
STL有六大組成部分,我們會來逐一學(xué)習(xí):
首先我們來學(xué)習(xí)容器中的string類。
2.string類的基本概念
(1)含義
string類,顧名思義是進(jìn)行字符串操作的一個類,操作方式即使用其中的成員函數(shù)來進(jìn)行操作。
(2)使用方法
#include<string> using namespace std;
string也是在類域std中的,我們在練習(xí)時也可以將類域std打開。
(3)原理
通過查閱文獻(xiàn),我們發(fā)現(xiàn)string類原來是typedef出來的,是對basic_string這一模板傳入char類型時起的別名。這一模板的定義方式大概是這樣的:
template<class T> class basic_string { private: T* _str; //.... }
有人會問為什么要使用模板呢?字符串類型除了char還有別的嘛?
其實(shí)字符類型除了char之外還有別的類型,比如wchar_t就占兩個字節(jié)。我們存儲漢字一般會使用兩個字節(jié)來進(jìn)行存儲。所以對于兩字節(jié)存儲的字符再使用basic_string傳入wchar_t起別名的話就方便的多了。
注意:string類中傳入的是char,上面只作了解即可。
3.string類中常見構(gòu)造函數(shù)
構(gòu)造函數(shù)即用于初始化。
1.string()
:構(gòu)造空的string類對象str,即空字符串。
2.string (const char* s)
:向?qū)ο笾袀魅胱址畞順?gòu)造string對象。
3.string(size_t n,char c)
:string類對象中包含n個字符c。
4.string(const string&s)
:拷貝構(gòu)造函數(shù)。
string s1;//空字符串s1。 string s2("hello world");//向s2中傳入hello world。 string s3(s2, 2, 6);//向s3中傳入s2中從第二個開始(不包括第二個)后六個字符。當(dāng)最后不加數(shù)值時(即不加6),則會取后面的全部字符。 string s4(s2);//用s2對s4進(jìn)行初始化。 cout << s1 << endl;//重載運(yùn)算符,可以直接打印s1對象的字符串內(nèi)容。 cout << s2 << endl; cout << s3 << endl; cout << s4 << endl;
4.string類中析構(gòu)函數(shù)
自動調(diào)用,不用管。
5.string類對象的容量操作
1.size
:返回有效字符長度。
2.length
:返回字符串的有效字符長度。是一個歷史殘留問題。
3.capacity
:返回空間總大小。
4.empty
:檢測字符串是否為空,是返回true,不是返回false
5.clear
:清空有效字符。
6.reserve
:為字符串預(yù)留空間。
7.resize
:將有效字符改成n個,多出的用字符C填充。
(1)顯示容量
string s1("hello world"); cout << s1.size() << endl;//輸出字符串大小,不包括'\0' cout << s1.length() << endl;//輸出字符串大小,不包括'\0' cout << s1.capacity() << endl;//輸出空間總大小 s1.clear();//清空有效字符串 cout << s1 << endl;
(2)擴(kuò)容
擴(kuò)容一般使用兩種函數(shù),reserve與resize。reserve主要為開空間,而resize在開空間的同時還會進(jìn)行初始化。
string s1("hello world"); cout << s1.capacity() << endl; s1.reserve(100); cout << s1.capacity() << endl;
打印的結(jié)果是:
通過打印我們發(fā)現(xiàn),reserve擴(kuò)展的空間是在原有字符串大小基礎(chǔ)上擴(kuò)展的,而不是在原有容量的基礎(chǔ)上。
string s1("hello world"); s1.resize(100,'x');
我們可以通過調(diào)試來觀察這段代碼的效果:
我們發(fā)現(xiàn)hello world并沒有被覆蓋,最大空間變成了111和reserve一樣。但是,x只有89個,一共增加了100-11個x,但是在hello world后面增加了100個空間。
如果我們開辟的空間小于字符串本身呢?s1.resize(5)表示的是只保留前五個字符大小。
6.string類中operator[]重載
(1)舉例
我們可以使用s1[i]來進(jìn)行字符的讀或者寫的操作。
string s1("hello world"); for (size_t i = 0; i < s1.size(); i++) { cout << s1[i] << " ";//s1[i]進(jìn)行讀操作 s1[i] += 1; } cout << endl; for (size_t i = 0; i < s1.size(); i++) { cout << s1[i] << " ";//s1[i]進(jìn)行寫操作 }
(2)底層實(shí)現(xiàn)
char& operator[](size_t pos) { return _str[pos];//返回pos位置所在的字符 }
注意,這里使用引用返回不是為了減少拷貝,這里是為了修改返回對象。
at也是和operator[]一樣,但是檢測越界的方法不同,operator[]使用斷言,at直接拋異常。
7.string類與迭代器
(1)舉例
在剛開始使用迭代器時,我們可以把它想象成一個指針類型。
string s1("hello world"); string::iterator it = s1.begin(); while (it != s1.end()) { cout << *it << " "; ++it; } cout << endl;
其中it就是一個迭代器類型的變量,由于函數(shù)iterator在類內(nèi),因此在使用時需要首先指明類域。
我們?nèi)匀皇褂媒庖貌僮鞣麃磉M(jìn)行類似解引用的操作。我們同樣也可以通過*來對字符串的內(nèi)容進(jìn)行修改,對這段代碼打印的結(jié)果是:
(2)反向迭代器
反向迭代器使用函數(shù)reverse_iterator
string s1("hello world"); string::reverse_iterator rit = s1.rbegin(); while (rit != s1.rend()) { cout << *rit; ++rit; }
打印的結(jié)果是剛好反過來的:
注意:我們定義反向迭代器rit之后,在進(jìn)行遍歷時雖然是從后向前遍歷,但是仍需要對rit進(jìn)行++操作。
(3)使用迭代的意義
迭代的意義主要體現(xiàn)在和[]進(jìn)行對比,有人可能認(rèn)為既然可以用下標(biāo)直接引用,為什么還要使用迭代器呢?
這是因?yàn)閷τ趕tring來說,下標(biāo)和[]就足夠好用,可以不使用迭代器,但是對于其他容器,比如list map/set等不支持下標(biāo)加[]遍歷,比如對于鏈表來說,由于物理內(nèi)存不連續(xù)所以不能使用下標(biāo)來進(jìn)行遍歷。
除了普通迭代器之外,還有const修飾的迭代器等等。后面會詳細(xì)說明的。
(4)補(bǔ)充:語法糖實(shí)現(xiàn)遍歷
除了使用迭代器遍歷之外,我們還可以使用語法糖來進(jìn)行遍歷操作:
string s1("hello world"); for (auto& e : s1) { cout << e << " "; }
范圍for會把s1中的每一個字符取出來賦值給e(使用引用則為它本身),自動往后迭代,自動判斷結(jié)束。
8.string類對象的增刪操作
push back
:在字符串后尾插字符c
append
:在字符串后追加一個字符串
operator+=
:在字符串后追加字符串str
c str
:返回C格式字符串
find+nops
:從字符串pos位置開始往后找字符c,返回該字符再字符串中的位置。
rfind
:從字符串中pos位置開始往前找字符c,返回該字符再字符串中的位置。
substr
:在str中從pos位置開始,截取n個字符,然后將其返回。
(1)增刪
string s1("hello world"); s1.push_back('a');//在末尾插入字符a cout << s1 << endl; s1.append("bcd");//在末尾插入字符串bcd cout << s1 << endl; s1 += 'e';//在末尾插入字符e cout << s1 << endl; s1 += "hello linux";//在末尾插入字符串hello linux cout << s1 << endl;
注意:推薦直接使用操作符重載進(jìn)行增刪操作。
(2)查與匹配
下面是一段字符串截取的代碼,截取后綴.txt:
string file("test.txt"); size_t pos = file.find('.');//返回第一次匹配'.'的位置 if (pos != string::npos) { string suffix = file.substr(pos, file.size() - pos);//將.之后的內(nèi)容賦值給suffix cout << suffix << endl; }
find會返回第一次出現(xiàn)匹配的位置,如果匹配失敗會返回nops(無符號-1,表示一個極大的數(shù))。
substr是字符串截取函數(shù),意思是截取pos位置與file.size()-pos位置的字符串。
同上,我們也可以使用rfind進(jìn)行倒著查找。
這里再舉一個截取網(wǎng)址的例子:
我們都知道網(wǎng)址分為三部分:協(xié)議,域名與網(wǎng)絡(luò)路徑。下面使用find與substr來進(jìn)行截取操作。
string url("http://www.cplusplus.com/reference/string/string/find/"); size_t pos1 = url.find(':'); string protocol = url.substr(0, pos1 - 0); cout << protocol << endl; size_t pos2 = url.find('/', pos1 + 3);//從pos+3的位置開始找 string domain = url.substr(pos1 + 3, pos2 - (pos1 + 3)); cout << domain << endl; string uri = url.substr(pos2 + 1);//沒指定結(jié)尾位置,默認(rèn)為整個后面內(nèi)容 cout << uri << endl;
打印的結(jié)果為:
9.string類中自定義插入與刪除(效率低,不建議使用)
使用insert與erase兩函數(shù)進(jìn)行操作:
string s("hello world"); s.insert(0, 1, 'x');//在0位置插入一個1 s.insert(s.begin(), 'y');//在頭處插入一個y s.insert(0, "test");//在頭處插入test s.insert(4, "&&");//在第四個位置插入特殊符號 s.erase(0, 1);//刪除頭處第一個字符 s.erase(s.size() - 1, 1);//尾刪一個字符 cout << s << endl;
打印的結(jié)果為:
在使用erase時,如果不給值的話,會繼續(xù)刪完。
盡量少使用頭部和中間的刪除,因?yàn)橐矂訑?shù)據(jù),效率較低。
10.string類字符串的比較
和C語言中strcmp一樣,string類也有對字符串的比較,不過是通過運(yùn)算符重載。
string s1("hello world"); string s2("string"); cout << (s1 < s2) << endl; cout << ("hhh" > s2) << endl;
注意我們可以使用兩種方式來說進(jìn)行字符串比較,一種是s1<s2,另一種是直接進(jìn)行比較。
與strcmp原理一樣,會從兩個字符串起始位置開始比較,如果為真則返回1,如果為假返回0。
11.string類字符串的轉(zhuǎn)換
我們使用stoi(string to int)來將字符串轉(zhuǎn)為整型,使用to_string來將其他類型轉(zhuǎn)換為字符串。
int val = stoi("1234"); string str = to_string(3.14);
我們可以通過調(diào)試來觀察轉(zhuǎn)換是否成功:
可見,轉(zhuǎn)換很成功。
12.總結(jié)
string類是表示字符串的字符串類,在使用string類時必須包含頭文件,并開放std空間,在OJ中有關(guān)字符串的題目基本以string類的形式出現(xiàn),而且在常規(guī)工作中,為了簡單、方便、快捷,基本都會使用string類,很少有人去使用C庫中的字符串操作函數(shù)。
本篇文章就到這里了,希望能夠給你帶來幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!
相關(guān)文章
C語言不使用strcpy函數(shù)如何實(shí)現(xiàn)字符串復(fù)制功能
這篇文章主要給大家介紹了關(guān)于C語言不使用strcpy函數(shù)如何實(shí)現(xiàn)字符串復(fù)制功能的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-02-02C++實(shí)現(xiàn)一個簡單消息隊(duì)列的示例詳解
消息隊(duì)列在多線程的場景有時會用到,尤其是線程通信跨線程調(diào)用的時候,就可以使用消息隊(duì)列進(jìn)行通信。本文將利用C++實(shí)現(xiàn)一個簡單的消息隊(duì)列,感興趣的可以了解一下2022-12-12