目錄
前言
1.什么是STL
2. STL的版本
3. STL的六大組件
4.STL的缺陷
5.string
5.1 為什么學(xué)習(xí)string類?
5.1.1 C語(yǔ)言中的字符串
5.2 標(biāo)準(zhǔn)庫(kù)中的string類
5.3 string類的常用接口的使用
5.3.1 構(gòu)造函數(shù)
5.3.2?string類對(duì)象的容量操作
5.3.3 string類對(duì)象的訪問(wèn)及遍歷操作
5.3.4?string類對(duì)象的修改操作
5.3.4?string類非成員函數(shù)
?編輯
總結(jié)
前言
? 大家好呀,失蹤兩個(gè)月的博主回來(lái)了,不要問(wèn)博主去干什么了?問(wèn)就是去修煉內(nèi)功心法去了,時(shí)至今日按照我們之前的更新進(jìn)度,我們應(yīng)該進(jìn)入到我們stl的相關(guān)內(nèi)容的學(xué)習(xí)。那么我們今天就從string開始逐漸給大家介紹我們的stl的內(nèi)容,但是在介紹之前,我們先籠統(tǒng)的了解一下我們stl的內(nèi)容。
1.什么是STL
STL(standard template libaray-標(biāo)準(zhǔn)模板庫(kù)):是C++標(biāo)準(zhǔn)庫(kù)的重要組成部分,不僅是一個(gè)可復(fù)用的組件庫(kù),而且是一個(gè)包羅數(shù)據(jù)結(jié)構(gòu)與算法的軟件框架。
2. STL的版本
原始版本:
Alexander Stepanov、Meng Lee 在惠普實(shí)驗(yàn)室完成的原始版本,本著開源精神,他們聲明允許任何人任意運(yùn)用、拷貝、修改、傳播、商業(yè)使用這些代碼,無(wú)需付費(fèi)。唯一的條件就是也需要向原始版本一樣做開源使用。 HP 版本--所有STL實(shí)現(xiàn)版本的始祖。
P. J. 版本:
由P. J. Plauger開發(fā),繼承自HP版本,被Windows Visual C++采用,不能公開或修改,缺陷:可讀性比較低,符號(hào)命名比較怪異。
RW版本:
由Rouge Wage公司(該公司已經(jīng)倒閉,因此該版本已經(jīng)多年沒有更新所以我們不用過(guò)度關(guān)注)開發(fā),繼承自HP版本,被C+ + Builder 采用,不能公開或修改,可讀性一般。
SGI版本:
由Silicon Graphics Computer Systems,Inc公司開發(fā),繼承自HP版 本。被GCC(Linux)采用,可移植性好,可公開、修改甚至販賣,從命名風(fēng)格和編程 風(fēng)格上看,閱讀性非常高。我們后面學(xué)習(xí)STL要閱讀部分源代碼,STL是規(guī)范,他有很多版本,其底層實(shí)現(xiàn)大同小異,但對(duì)于某些接口其還是有著不同的主要參考的就是這個(gè)版本。
3. STL的六大組件
這里的容器就是我們的數(shù)據(jù)結(jié)構(gòu),然后空間適配器就是我們的內(nèi)存池。
4.STL的缺陷
1. STL庫(kù)的更新太慢了。這個(gè)得嚴(yán)重吐槽,上一版靠譜是C++98,中間的C++03基本一些修訂。C++11出來(lái)已經(jīng)相隔了13年,STL才進(jìn)一步更新。
2. STL現(xiàn)在都沒有支持線程安全。并發(fā)環(huán)境下需要我們自己加鎖。且鎖的粒度是比較大的。
3. STL極度的追求效率,導(dǎo)致內(nèi)部比較復(fù)雜。比如類型萃取,迭代器萃取。
4. STL的使用會(huì)有代碼膨脹的問(wèn)題,比如使用vector/vector/vector這樣會(huì)生成多份代碼,當(dāng)然這是模板語(yǔ)法本身導(dǎo)致的。
5.string
5.1 為什么學(xué)習(xí)string類?
5.1.1 C語(yǔ)言中的字符串
C語(yǔ)言中,字符串是以'\0'結(jié)尾的一些字符的集合,為了操作方便,C標(biāo)準(zhǔn)庫(kù)中提供了一些str系列的庫(kù)函數(shù),但是這些庫(kù)函數(shù)與字符串是分離開的,不太符合OOP(面向?qū)ο?的思想,而且底層空間需要用戶自己管理,稍不留神可能還會(huì)越界訪問(wèn)。
5.2 標(biāo)準(zhǔn)庫(kù)中的string類
1. 字符串是表示字符序列的類
2. 標(biāo)準(zhǔn)的字符串類提供了對(duì)此類對(duì)象的支持,其接口類似于標(biāo)準(zhǔn)字符容器的接口,但添加了專門用于操作單字節(jié)字符字符串的設(shè)計(jì)特性。
3. string類是使用char(即作為它的字符類型,使用它的默認(rèn)char_traits和分配器類型(關(guān)于模板的更多信息,請(qǐng)參閱basic_string)。
4. string類是basic_string模板類的一個(gè)實(shí)例,它使用char來(lái)實(shí)例化basic_string模板類,并用char_traits和allocator作為basic_string的默認(rèn)參數(shù)(根于更多的模板信息請(qǐng)參basic_string)。
5. 注意,這個(gè)類獨(dú)立于所使用的編碼來(lái)處理字節(jié):如果用來(lái)處理多字節(jié)或變長(zhǎng)字符(如UTF-8)的序列,這個(gè)類的所有成員(如長(zhǎng)度或大小)以及它的迭代器,將仍然按照字節(jié)(而不是實(shí)際編碼的字符)來(lái)操作
總結(jié):
1. string是表示字符串的字符串類
2. 該類的接口與常規(guī)容器的接口基本相同,再添加了一些專門用來(lái)操作string的常規(guī)操作
3. string在底層實(shí)際是:basic_string模板類的別名,typedef basic_string<char, char_traits, allocator>? string;
4. 不能操作多字節(jié)或者變長(zhǎng)字符的序列。
5.3 string類的常用接口的使用
注意:小編介紹的都是我們常用接口,對(duì)于具體的還需要我們自己去查閱文檔進(jìn)行深度學(xué)習(xí)。
5.3.1 構(gòu)造函數(shù)
這里我們常用的就這幾個(gè),這里我們簡(jiǎn)單給大家演示一下該使用:
#include<iostream>
#include<string>//使用記得包文件
using namespace std;
int main()
{
string s1; // 構(gòu)造空的string類對(duì)象s1
string s2("hello xhj"); // 用C格式字符串構(gòu)造string類對(duì)象s2
string s3(s2); // 拷貝構(gòu)造s3
string s4(10, 'c');//使用n個(gè)字符,構(gòu)造我們的對(duì)象
//注意我這里打印,是需要重載我們的流插入操作符的,之后我們也會(huì)在對(duì)string類的實(shí)現(xiàn)給大家介紹,
//這里為了大家更加直觀的感受,我們這里先直接使用即可
cout << s1 << endl;//在有些編譯器下就算訪問(wèn)到string類的‘\0'其也不會(huì)顯示的打印出來(lái)。
cout << s2 << endl;
cout << s3 << endl;
cout << s4 << endl;
return 0;
}
結(jié)果展示:
5.3.2?string類對(duì)象的容量操作
注意:
1. size()與length()方法底層實(shí)現(xiàn)原理完全相同,引入size()的原因是為了與其他容器的接口保持一致,一般情況下基本都是用size()。(max_size這個(gè)接口不同編譯器是不同的結(jié)果,所以該使用是沒有很大意義的)
2. clear()只是將string中有效字符清空,不改變底層空間大小。
3. resize(size_t n) 與 resize(size_t n, char c)都是將字符串中有效字符個(gè)數(shù)改變到n個(gè),不同的是當(dāng)字符個(gè)數(shù)增多時(shí):resize(n)用/0來(lái)填充多出的元素空間(size也會(huì)發(fā)生改變),resize(size_t n, char c)用字符c來(lái)填充多出的元素空間。注意:resize在改變?cè)貍€(gè)數(shù)時(shí),如果是將元素個(gè)數(shù)增多,可能會(huì)改變底層容量的大小,如果是將元素個(gè)數(shù)減少,底層空間總大小不變。(因?yàn)閷?duì)于縮容來(lái)講是有代價(jià)的,在我們學(xué)習(xí)中并沒有對(duì)一塊空間部分釋放的函數(shù)(系統(tǒng)不支持分段釋放空間),所以該實(shí)際上進(jìn)行的操作其實(shí)是將數(shù)據(jù)內(nèi)容拷貝到一個(gè)新的縮小后的·空間)
4. reserve(size_t res_arg=0):為string預(yù)留空間,不改變有效元素個(gè)數(shù),當(dāng)reserve的參數(shù)小于string的底層空間總大小時(shí),reserve改不改變?nèi)Q于不同的編譯器和我們string類中是否有數(shù)據(jù),也有可能不縮?。ㄔ摬痪哂屑s束力)。
(對(duì)于reserve來(lái)講不同的編譯器對(duì)于編譯過(guò)程中的擴(kuò)容機(jī)制是不一樣的,對(duì)于vs是在原來(lái)的基礎(chǔ)上加上擴(kuò)容的大小,Linux下是直接擴(kuò)到指定大小,但該本質(zhì)上都不會(huì)小于我們要求的大小)
此外這里我們的shrink_to_fit,這個(gè)函數(shù)的作用是將string的capacity減到size大?。ǖ沁@個(gè)函數(shù)是不具有約束力的,所以這里也不一定會(huì)縮到size大?。?,這里我們做個(gè)簡(jiǎn)單的了解即可。
代碼演示:
#include<iostream>
#include<string>//使用記得包文件
using namespace std;
int main()
{
string s1;
s1.reserve(100);//開容量
cout << s1.size() << endl;
cout << s1.capacity() << endl;
s1.resize(10);//開有效數(shù)據(jù)的空間
cout << s1.size() << endl;
cout << s1.capacity() << endl;
s1.clear();//清空有效數(shù)據(jù)
cout << s1.size() << endl;
cout << s1.capacity() << endl;
}
結(jié)果:
5.3.3 string類對(duì)象的訪問(wèn)及遍歷操作
這里我們的遍歷方式是有多種的,但是對(duì)于該底層如何是實(shí)現(xiàn)的,我們待會(huì)再了解,這里先給大家演示一下,該遍歷方式:
#include<iostream>
#include<string>//使用記得包文件
using namespace std;
int main()
{
string s1("hello world!");
//[]遍歷
for (size_t i = 0; i < s1.size(); i++)
{
cout << s1[i] << " ";
}
cout << endl;
//正向迭代器的遍歷
string::iterator it = s1.begin();
while (it != s1.end())
{
cout << *it << " ";
it++;
}
cout << endl;
//反向迭代器的遍歷
string::reverse_iterator it1 = s1.rbegin();
while (it1 != s1.rend())
{
cout << *it1 << " ";
it1++;
}
cout << endl;
//范圍for
for (auto e : s1)
{
cout << e << " ";
}
cout << endl;
}
結(jié)果展示:
這里我們可以看到反向迭代器是一種從后往前的遍歷方式,至于該具體實(shí)現(xiàn),我們以后再和大家講解。此外,我們需要了解一下以下的相關(guān)知識(shí):
1.對(duì)于,[]我們出現(xiàn)數(shù)組越界采用的是斷言的方式去進(jìn)行處理的
2.對(duì)于內(nèi)置類型和自定義類型該[],是具有本質(zhì)的區(qū)別的,對(duì)于內(nèi)置類型該是一個(gè)解引用操作,也就是 *(a+i),但是對(duì)于內(nèi)置類型,其實(shí)s.operator[](i);,因此對(duì)于我們的自定義類型我們是需要對(duì)其進(jìn)行運(yùn)算符重載的。
3.迭代器(任何容器都支持迭代器,并且用法都是類似的,而我們的[]只支持底層是數(shù)組的容器)
對(duì)于迭代器,上面我們只講述了該用法,但是對(duì)于迭代器,我們需要了解更多一點(diǎn)該相關(guān)內(nèi)容:
literator提供一種統(tǒng)一的方式訪問(wèn)和修改容器數(shù)據(jù) ,算法可以通過(guò)迭代器,去處理容器中數(shù)據(jù)
此外我們的迭代器一共分為四種:
一種是普通變量的正向迭代器,一種是const變量的正向迭代器,對(duì)于const變量我們只能使用const迭代器(涉及到我們的權(quán)限問(wèn)題)而且我們的普通變量的迭代器可以讀寫,而我們的const迭代器只能讀取
另外兩種就是反項(xiàng)的兩個(gè)迭代器
5.3.4?string類對(duì)象的修改操作
這里我們的append和我們的+=的效果是一樣的,都可以起到在我們對(duì)象后面加上一個(gè)字符串和單個(gè)字符的效果(這是由于函數(shù)重載得到的),而我們的push_back只能在后面加上一個(gè)字符。
因此:
1. 在string尾部追加字符時(shí),s.push_back(c) / s.append(1, c) / s += 'c'三種的實(shí)現(xiàn)方式差不多,一般情況下string類的+=操作用的比較多,+=操作不僅可以連接單個(gè)字符,還可以連接字符串。
2. 對(duì)string操作時(shí),如果能夠大概預(yù)估到放多少字符,可以先通過(guò)reserve把空間預(yù)留好。(編譯器也會(huì)自動(dòng)擴(kuò)容,但是我們知道數(shù)據(jù)個(gè)數(shù)的情況下會(huì)直接擴(kuò)容會(huì)減少成本)
這里簡(jiǎn)單的給大家演示一下這三個(gè)函數(shù)的使用方式:
#include<iostream>
#include<string>//使用記得包文件
using namespace std;
int main()
{
string s1("hello");
s1 += 'w';
string s2("hello");
s2.append(1,'w');
string s3("hello");
s3.push_back('w');
cout << s1 << endl;
cout << s2 << endl;
cout << s3 << endl;
string s4("hello");
string s5("hello");
s4.append("world");
s5 += "world";
cout << s4 << endl;
cout << s5 << endl;
return 0;
}
結(jié)果如下:
這里我們的insert,erase,以及我們的replace,我們都不是經(jīng)常使用,這里原因是由于這里帶來(lái)的效率過(guò)于低下,那么我們只需要對(duì)其做個(gè)簡(jiǎn)單的認(rèn)識(shí)即可。
insert:
insert的作用是在我們指定的位置進(jìn)行插入,這里有一個(gè)下標(biāo)版本和一個(gè)迭代器版本,但是不建議經(jīng)常使用insert因?yàn)檫@里涉及到效率的問(wèn)題
erase:
這里我們的npos是:
這里指的是一個(gè)靜態(tài)成員,實(shí)際上該是無(wú)符號(hào)數(shù)的-1,也就是說(shuō)使用該值后就會(huì)自動(dòng)刪除pos位置后的所有字符。
erase由于是在中間或者頭部刪除數(shù)據(jù)的所以效率也是比較低的我們不推薦。
replace:
對(duì)于替換這個(gè)函數(shù)我們這里也不常使用因?yàn)檫@里需要消耗的代價(jià)太大。
這里我們就簡(jiǎn)單的給大家簡(jiǎn)單的演示一下,該使用方式即可:
#include<iostream>
#include<string>//使用記得包文件
using namespace std;
int main()
{
string s1("hello");
s1.insert(2, "xxx");
cout << s1 << endl;
s1.erase(2, 3);
cout << s1 << endl;
s1.replace(0, 5, "world");
cout << s1 << endl;
return 0;
}
運(yùn)行結(jié)果如下:
然后就是我們的pop_back了,這里就是將最后的一個(gè)字符刪除即可,大家可以自己演示一下。
接下來(lái)我們需要了解的一個(gè)接口是我們的c_str,這里返回的是C語(yǔ)言形式的字符串,那么具體需要了解該是什么,我們需要看一下一下一段代碼:
這里的cout<<s1<<endl;和cout<<s1.c_str();這兩個(gè)得到的值是一致的,但實(shí)際上該一個(gè)是流提取操作符的重載,一個(gè)相當(dāng)于打印的是char*(也就是返回C語(yǔ)言的字符串,因此這個(gè)函數(shù)的作用是將C語(yǔ)言和c++進(jìn)行有些接口進(jìn)行配合)
緊接著我們需要了解的是我們的find接口,和我們的rfind接口
這里注意我們的主要作用是:從字符串pos位置開始往后找字符c,返回該字符在字符串中的位置
這里注意其返回值是:
與此同時(shí)我們的rfind的作用是:從字符串pos位置開始往前找字符c,返回該字符在字符串中的位置
這里我們的find和我們的rfind的使用方式是一致的,那么我們這里就給大家演示其中一個(gè)的使用方式。
最后一個(gè)我們經(jīng)常使用的接口就是,substr,該作用就是:在str中從pos位置開始,截取n個(gè)字符,然后將其返回。
這里我們返回的也是一個(gè)string對(duì)象,該使用方式如下:
5.3.4?string類非成員函數(shù)
這里我只給大家介紹一個(gè)getline接口,對(duì)于其他接口,我們?cè)谶M(jìn)行模擬實(shí)現(xiàn)后,大家就可以輕而易舉的理解了。
理解我們的getline首先我們需要知道一個(gè)小的知識(shí)點(diǎn),這里我們配合一段代碼講解一下:
#include<iostream>
#include<string>//使用記得包文件
using namespace std;
int main()
{
string s1;
cin >> s1;
cout << s1;
return 0;
}
運(yùn)行結(jié)果:
這里我們輸入的明明是我們的:hello world,但是實(shí)際上我們s1是hello,那么是什么原因?qū)е逻@樣呢?
這是由于cin在逐個(gè)輸入數(shù)據(jù)時(shí)是以空格或者換行來(lái)辨別數(shù)據(jù)的,所以在逐個(gè)輸入數(shù)據(jù)時(shí),我們的空格或者換行是不能被讀取的如果我們想要讀取換行就需要使用到istream中的getline函數(shù)
getline是非成員函數(shù),進(jìn)行的是輸入操作,默認(rèn)這里遇到換行符結(jié)束輸入字符串。
這里我給大家簡(jiǎn)單的演示一下:
文章最后,我們需要了解的是,與我們string相關(guān)的最后一類接口:
這里就不給大家一個(gè)一個(gè)具體介紹了,這里大家需要記住的是我們經(jīng)常使用的兩個(gè):
我們的to_string就是將我們其他類型轉(zhuǎn)換為我們的string類型,我們的stoi就是將我們的string類型轉(zhuǎn)化為我們的整型。文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-727441.html
總結(jié)
到這里我們對(duì)string各個(gè)常用接口的介紹已經(jīng)到這里了,但是我們一定要經(jīng)常使用,才會(huì)銘記于心,后續(xù)為了加強(qiáng)大家的理解,小編會(huì)給大家?guī)?lái)我們string類的相關(guān)模擬實(shí)現(xiàn)。請(qǐng)大家敬請(qǐng)期待。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-727441.html
到了這里,關(guān)于string類的使用方式的介紹的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!