吉林省最新疫情最新消息優(yōu)化大師官網(wǎng)登錄入口
文章目錄
- 前言
- 一、 為什么學(xué)習(xí)string類(lèi)
- 二、標(biāo)準(zhǔn)庫(kù)中的string類(lèi)
前言
👧個(gè)人主頁(yè):@小沈YO.
😚小編介紹:歡迎來(lái)到我的亂七八糟小星球🌝
📋專(zhuān)欄:C++ 心愿便利店
🔑本章內(nèi)容:string
記得 評(píng)論📝 +點(diǎn)贊👍 +收藏😽 +關(guān)注💞哦~
提示:以下是本篇文章正文內(nèi)容,下面案例可供參考
一、 為什么學(xué)習(xí)string類(lèi)
C語(yǔ)言中,字符串是以’\0’結(jié)尾的一些字符的集合,為了操作方便,C標(biāo)準(zhǔn)庫(kù)中提供了一些str系列的庫(kù)函數(shù),但是這些庫(kù)函數(shù)與字符串是分離開(kāi)的,不太符合OOP的思想,而且底層空間需要用戶自己管理,稍不留神可能還會(huì)越界訪問(wèn)
開(kāi)始學(xué)習(xí)string就需要開(kāi)始學(xué)習(xí)讀文檔具體可以通過(guò)cplusplus.con網(wǎng)站去搜索
二、標(biāo)準(zhǔn)庫(kù)中的string類(lèi)
🌟string
- 字符串是表示字符序列的類(lèi)
- 標(biāo)準(zhǔn)的字符串類(lèi)提供了對(duì)此類(lèi)對(duì)象的支持,其接口類(lèi)似于標(biāo)準(zhǔn)字符容器的接口,但添加了專(zhuān)門(mén)用于操作單字節(jié)字符字符串的設(shè)計(jì)特性。
- string類(lèi)是使用char(即作為它的字符類(lèi)型,使用它的默認(rèn)char_traits和分配器類(lèi)型(關(guān)于模板的更多信息,請(qǐng)參閱basic_string)。
- string類(lèi)是basic_string模板類(lèi)的一個(gè)實(shí)例,它使用char來(lái)實(shí)例化basic_string模板類(lèi),并用char_traits和allocator作為basic_string的默認(rèn)參數(shù)(根于更多的模板信息請(qǐng)參考basic_string)。
- 注意,這個(gè)類(lèi)獨(dú)立于所使用的編碼來(lái)處理字節(jié):如果用來(lái)處理多字節(jié)或變長(zhǎng)字符(如UTF-8)的序列,這個(gè)類(lèi)的所有成員(如長(zhǎng)度或大小)以及它的迭代器,將仍然按照字節(jié)(而不是實(shí)際編碼的字符)來(lái)操作。
總結(jié):
- string是表示字符串的字符串類(lèi)
- 該類(lèi)的接口與常規(guī)容器的接口基本相同,再添加了一些專(zhuān)門(mén)用來(lái)操作string的常規(guī)操作
- string在底層實(shí)際是:basic_string模板類(lèi)的別名,typedef basic_string<char, char_traits, allocator> string;
- 不能操作多字節(jié)或者變長(zhǎng)字符的序列
在使用string類(lèi)時(shí),必須包含#include頭文件以及using namespace std;
(constructor)函數(shù)名稱 | 功能說(shuō)明 |
---|---|
string() (重點(diǎn)) | 構(gòu)造空的string類(lèi)對(duì)象,即空字符串 |
string(const char* s) (重點(diǎn)) | 用C-string來(lái)構(gòu)造string類(lèi)對(duì)象 |
string(size_t n, char c) | string類(lèi)對(duì)象中包含n個(gè)字符c |
string(const string&s) (重點(diǎn)) | 拷貝構(gòu)造函數(shù) |
- string() (重點(diǎn)) - - - 構(gòu)造空的string類(lèi)對(duì)象,即空字符串
void test_string1()
{string s1;cout<<s1<<endl;
}
int main()
{test_string1();return 0;
}
- string(const char* s) (重點(diǎn))- - - 用C-string來(lái)構(gòu)造string類(lèi)對(duì)象
void test_string1()
{string s2("hello");cout << s2 << endl;
}
int main()
{test_string1();return 0;
}
- string(size_t n, char c) string類(lèi)對(duì)象中包含n個(gè)字符c
int main()
{string s1(3, 'a');cout << s1 << endl;return 0;
}
- string(const string&s) (重點(diǎn)) 拷貝構(gòu)造函數(shù)
int main()
{string s1("hello");string s2(s1);cout << s1 << endl;cout << s2 << endl;return 0;
}
函數(shù)名稱 | 功能說(shuō)明 |
---|---|
size(重點(diǎn)) | 返回字符串有效字符長(zhǎng)度 |
length | 返回字符串有效字符長(zhǎng)度 |
capacity | 返回空間總大小 |
empty (重點(diǎn)) | 檢測(cè)字符串釋放為空串,是返回true,否則返回false |
clear (重點(diǎn)) | 清空有效字符 |
reserve (重點(diǎn)) | 為字符串預(yù)留空間(確定大概知道要多少空間,提前開(kāi)好,減少擴(kuò)容,提高效率) |
resize (重點(diǎn)) | 將有效字符的個(gè)數(shù)改成n個(gè),多出的空間用字符c填充 |
shrink_to_fit | 將capacity容量縮至合適 |
- size(重點(diǎn))- - - 返回字符串有效字符長(zhǎng)度
int main()
{string s1("hello");string s2("aaaaaaaaaaaa");cout << s1.size() << endl;cout << s2.size() << endl;return 0;
}
- length - - - 返回字符串有效字符長(zhǎng)度
int main()
{string s1("hello");string s2("aaaaaaaaaaaa");cout << s1.length() << endl;cout << s2.length() << endl;return 0;
}
- capacity - - - 返回空間總大小
int main()
{string s1("hello");string s2("aaaaaaaaaaaa");cout << s1.capacity () << endl;cout << s2.capacity ()<< endl;return 0;
}
同一個(gè)string對(duì)象,在不同平臺(tái)下的capacity()(空間容量)可能不同,因?yàn)閟tring在底層就是一個(gè)存儲(chǔ)字符的動(dòng)態(tài)順序表,空間不夠了要進(jìn)行擴(kuò)容,而不同平臺(tái)底層的擴(kuò)容機(jī)制有所不同,導(dǎo)致了最終capacity()的結(jié)果不同。例如下述展現(xiàn)的擴(kuò)容機(jī)制:
🌟VS下的擴(kuò)容機(jī)制: 第一次擴(kuò)容是2倍,后面都是以1.5倍的大小去擴(kuò)容。
void test_string1()
{string s;size_t old = s.capacity();cout << "初始" << s.capacity() << endl;for (size_t i = 0; i < 100; i++){s.push_back('a');if (s.capacity() != old){cout << "擴(kuò)容:" << s.capacity() << endl;old = s.capacity();}}cout << s.capacity() << endl;
}
🌟Linux下的擴(kuò)容機(jī)制: 一次按照2倍的大小進(jìn)行擴(kuò)容
void test_string1()
{string s;size_t old = s.capacity();cout << "初始" << s.capacity() << endl;for (size_t i = 0; i < 100; i++){s.push_back('a');if (s.capacity() != old){cout << "擴(kuò)容:" << s.capacity() << endl;old = s.capacity();}}cout << s.capacity() << endl;
}
- empty (重點(diǎn))- - - 檢測(cè)字符串釋放為空串,是返回true,否則返回false
int main()
{string s1;if (s1.empty()){cout << "s1是一個(gè)空字符串" << endl;}return 0;
}
- clear (重點(diǎn))- - - 清空有效字符
void test_string8()
{string s1("hello world");cout << s1.size() << endl;//11cout << s1.capacity() << endl;//15s1.clear();cout << s1.size() << endl;//0cout << s1.capacity() << endl;//15
}
- reserve (重點(diǎn))- - - 為字符串預(yù)留空間(確定大概知道要多少空間,提前開(kāi)好,減少擴(kuò)容,提高效率)
void test_string1()
{string s;s.reserve(100);size_t old = s.capacity();cout << "初始" << s.capacity() << endl;for (size_t i = 0; i < 100; i++){s.push_back('a');if (s.capacity() != old){cout << "擴(kuò)容:" << s.capacity() << endl;old = s.capacity();}}s.reserve(10);cout << s.capacity() << endl;
}
如上當(dāng)不寫(xiě)s.reserve(100);就會(huì)發(fā)生擴(kuò)容,但是當(dāng)寫(xiě)上s.reserve(100);提前開(kāi)好空間就不會(huì)發(fā)生擴(kuò)容,同時(shí)要注意s.reserve(10);并不會(huì)縮減空間(縮容)
- resize (重點(diǎn))- - - 將有效字符的個(gè)數(shù)改成n個(gè),多出的空間用字符c填充
void test_string2()
{string s1("hello world");cout << s1 << endl;cout << s1.size() << endl;cout << s1.capacity() << endl;//s1.resize(13, 'x');s1.resize(20, 'x');//這里有效字符個(gè)數(shù)改成20s1本來(lái)的有效字符hello world是11個(gè)超出部分用x補(bǔ)充,其次size()和capacity也會(huì)隨之發(fā)生改變--->size()變成20;capacity()變成31s1.resize(5);cout << s1 << endl;cout << s1.size() << endl;cout << s1.capacity() << endl;string s2;s2.resize(10, '#');cout << s2 << endl;cout << s2.size() << endl;cout << s2.capacity() << endl;
}
- shrink_to_fit() - - - 將capacity容量縮至合適
void test_string1()
{string s;s.reserve(50);s += "hello world";cout << s.size() << endl;cout << s.capacity() << endl;s.shrink_to_fit();cout << s.size() << endl;cout << s.capacity() << endl;
}
注意:
- size()與length()方法底層實(shí)現(xiàn)原理完全相同,引入size()的原因是為了與其他容器的接口保持一致,一般情況下基本都是用size()。
- clear()只是將string中有效字符清空,不改變底層空間大小。
- resize(size_t n) 與 resize(size_t n, char c)都是將字符串中有效字符個(gè)數(shù)改變到n個(gè),不同的是當(dāng)字符個(gè)數(shù)增多時(shí):resize(n)用0來(lái)填充多出的元素空間,resize(size_t n, char c)用字符c來(lái)填充多出的元素空間。注意:resize在改變?cè)貍€(gè)數(shù)時(shí),如果是將元素個(gè)數(shù)增多,可能會(huì)改變底層容量的大小,如果是將元素個(gè)數(shù)減少,底層空間總大小不變。
- reserve(size_t res_arg=0):為string預(yù)留空間,不改變有效元素個(gè)數(shù),當(dāng)reserve的參數(shù)小于string的底層空間總大小時(shí),reserver不會(huì)改變?nèi)萘看笮?/strong>。
函數(shù)名稱 | 功能說(shuō)明 |
---|---|
operator[] (重點(diǎn)) | 返回pos位置的字符,const string類(lèi)對(duì)象調(diào)用 |
begin+ end | begin獲取一個(gè)字符的迭代器 + end獲取最后一個(gè)字符下一個(gè)位置的迭代器 |
rbegin + rend | begin獲取一個(gè)字符的迭代器 + end獲取最后一個(gè)字符下一個(gè)位置的迭代器 |
范圍for | C++11支持更簡(jiǎn)潔的范圍for的新遍歷方式 |
- operator[] (重點(diǎn)) 返回pos位置的字符,const string類(lèi)對(duì)象調(diào)用
void test_string2()
{string s1("hello world\n");string s2 = "hello world";//單參數(shù)構(gòu)造支持隱式類(lèi)型轉(zhuǎn)換//遍歷stringfor (size_t i = 0; i < s1.size(); i++){//讀cout << s1[i] << " ";}cout << endl;for (size_t i = 0; i < s1.size(); i++){//寫(xiě)s1[i]++;}cout << s1 << endl;;
}
- begin+ end - - - begin獲取一個(gè)字符的迭代器 + end獲取最后一個(gè)字符下一個(gè)位置的迭代器
void test_string3()
{string s4="hello world";string::iterator it = s4.begin();while (it != s4.end()){//讀cout << *it << " ";it++;}cout << endl;it = s4.begin();//while (it < s4.end())可以這樣寫(xiě)但是不建議while (it != s4.end()){//寫(xiě)*it='a';cout << *it << " ";it++;}cout << endl;
}
- rbegin + rend - - - begin獲取一個(gè)字符的迭代器 + end獲取最后一個(gè)字符下一個(gè)位置的迭代器
void test_string4()
{string s5 = "hello world";string::reverse_iterator rit = s5.rbegin();while (rit != s5.rend()){cout << *rit << " ";++rit;}cout << endl;
}
- 范圍for - - - C++11支持更簡(jiǎn)潔的范圍for的新遍歷方式
void test_string5()
{string s6 = "hello world";//原理:編譯器替換成迭代器for (auto ch : s6){//讀cout << ch << " ";}cout << endl;//對(duì)于寫(xiě)---范圍for 本質(zhì)自動(dòng)遍歷是*it賦值給ch,ch是*it的拷貝所以要寫(xiě)的話要加&,這樣ch就是*it的別名for (auto ch : s6)//錯(cuò)誤寫(xiě)法for (auto& ch : s6){//寫(xiě)ch++;}cout << s6 << " ";cout << endl;}
void func(const string s)//不推薦傳值傳參,會(huì)進(jìn)行拷貝調(diào)用拷貝構(gòu)造string的底層不能用淺拷貝所以用引用+constvoid func(const string& s)
{//迭代器支持讀寫(xiě),但是這里是const不支持迭代器寫(xiě)所以C++設(shè)計(jì)出了cbegin() cend() crbegin() crend()也可以+const例如下面一行注釋代碼//string::const_iterator it = s.begin();//對(duì)比于上面代碼+const和不+const還用修改可以直接使用auto直接推出類(lèi)型auto it = s.begin();while (it != s.end()){//讀cout << *it << " ";it++;}cout << endl;//string::const_reverse_iterator rit = s.rbegin();auto rit = s.rbegin();while (rit != s.rend()){cout << *rit << " ";++rit;}cout << endl;
}
void test_string6()
{string s7 = "hello world";func(s7);
}
函數(shù)名稱 | 功能說(shuō)明 |
---|---|
push_back | 在字符串后尾插字符c |
append | 在字符串后追加一個(gè)字符串 |
operator+= (重點(diǎn)) | 在字符串后追加字符串str |
c_str(重點(diǎn)) | 返回C格式字符串 |
find + npos(重點(diǎn)) | 從字符串pos位置開(kāi)始往后找字符c,返回該字符在字符串中的位置 |
rfind | 從字符串pos位置開(kāi)始往前找字符c,返回該字符在字符串中的位置 |
substr | 在str中從pos位置開(kāi)始,截取n個(gè)字符,然后將其返回 |
- push_back - - - 在字符串后尾插字符c
void test_string3()
{string s;string ss("hello");s.push_back('#');cout <<s<< endl;
}
- append - - - 在字符串后追加一個(gè)字符串
void test_string3()
{string s;string ss("hello");s.append("hello world");s.append(ss);cout <<s<< endl;
}
- operator+= (重點(diǎn)) - - - 在字符串后追加字符串str
void test_string3()
{string s;string ss("hello");s += '#';s += "hello";s += ss;cout << s << endl;
}
- c_str(重點(diǎn)) - - - 返回C格式字符串
void test_string9()
{string filename;cin >> filename;FILE* fout = fopen(filename.c_str(), "r");
}
- find + npos(重點(diǎn)) - - - 從字符串pos位置開(kāi)始往后找字符c,返回該字符在字符串中的位置
void test_string2()
{string s1("test.cpp");//讀取文件后綴size_t i = s1.find('.');string s3 = s1.substr(i);cout << s3 << endl;
}
- rfind - - - 從字符串pos位置開(kāi)始往前找字符c,返回該字符在字符串中的位置
void test_string2()
{string s2("test.cpp.tar.zip");//找文件后綴.zipsize_t j = s2.rfind('.');//倒著找string s4 = s2.substr(j);cout << s4 << endl;
}
- substr - - - 在str中從pos位置開(kāi)始,截取n個(gè)字符,然后將其返回
void test_string2()
{string s1("test.cpp");//讀取文件后綴size_t i = s1.find('.');string s3 = s1.substr(i);cout << s3 << endl;
}
注意:
- 在string尾部追加字符時(shí),s.push_back? / s.append(1, c) / s += 'c’三種的實(shí)現(xiàn)方式差不多,一般情況下string類(lèi)的+=操作用的比較多,+=操作不僅可以連接單個(gè)字符,還可以連接字符串。
- 對(duì)string操作時(shí),如果能夠大概預(yù)估到放多少字符,可以先通過(guò)reserve把空間預(yù)留好。
函數(shù) | 功能說(shuō)明 |
---|---|
operator+ | 盡量少用,因?yàn)閭髦捣祷?#xff0c;導(dǎo)致深拷貝效率低 |
operator>> (重點(diǎn)) | 輸入運(yùn)算符重載 |
operator<< (重點(diǎn)) | 輸出運(yùn)算符重載 |
getline (重點(diǎn)) | 獲取一行字符串 |
relational operators (重點(diǎn)) | 大小比較 |
- operator+ - - - 盡量少用,因?yàn)閭髦捣祷?#xff0c;導(dǎo)致深拷貝效率低
void test_string3()//at失敗后會(huì)拋異常
{string ss("hello");string ret=ss + '#';//+是一個(gè)傳值返回,代價(jià)比較大string ret2 = ss + "hello";cout << ret << endl;cout << ret2 << endl;cout << endl;
}
- operator>> (重點(diǎn))- - - 輸入運(yùn)算符重載
- operator<< (重點(diǎn)) - - - 輸出運(yùn)算符重載
- getline (重點(diǎn)) - - - 獲取一行字符串
🌟cin>> 和getline的區(qū)別在于:>>遇到空格’ '和換行\(zhòng)n會(huì)截止,而getline默認(rèn)只有遇到換行\(zhòng)n才截止,因此當(dāng)我們需要從鍵盤(pán)讀取一個(gè)含有空格的字符串是,只能用getline
void test_string3()
{string s1;getline(cin, s1, '!');cout << s1;
}
- relational operators (重點(diǎn))- - - 大小比較
-
string類(lèi)型轉(zhuǎn)換成內(nèi)置類(lèi)型
-
內(nèi)置類(lèi)型轉(zhuǎn)換成string
注意:下述結(jié)構(gòu)是在32位平臺(tái)下進(jìn)行驗(yàn)證,32位平臺(tái)下指針占4個(gè)字節(jié)
vs下string的結(jié)構(gòu)
string總共占28個(gè)字節(jié),內(nèi)部結(jié)構(gòu)稍微復(fù)雜一點(diǎn),先是有一個(gè)聯(lián)合體,聯(lián)合體用來(lái)定義string中字
符串的存儲(chǔ)空間:
當(dāng)字符串長(zhǎng)度小于16時(shí),使用內(nèi)部固定的字符數(shù)組來(lái)存放
當(dāng)字符串長(zhǎng)度大于等于16時(shí),從堆上開(kāi)辟空間
union _Bxty
{ // storage for small buffer or pointer to larger onevalue_type _Buf[_BUF_SIZE];pointer _Ptr;char _Alias[_BUF_SIZE]; // to permit aliasing
} _Bx;
這種設(shè)計(jì)也是有一定道理的,大多數(shù)情況下字符串的長(zhǎng)度都小于16,那string對(duì)象創(chuàng)建好之后,內(nèi)部已經(jīng)有了16個(gè)字符數(shù)組的固定空間,不需要通過(guò)堆創(chuàng)建,效率高。
其次:還有一個(gè)size_t字段保存字符串長(zhǎng)度,一個(gè)size_t字段保存從堆上開(kāi)辟空間總的容量
最后:還有一個(gè)指針做一些其他事情。
故總共占16+4+4+4=28個(gè)字節(jié)
g++下string的結(jié)構(gòu)
G++下,string是通過(guò)寫(xiě)時(shí)拷貝實(shí)現(xiàn)的,string對(duì)象總共占4個(gè)字節(jié),內(nèi)部只包含了一個(gè)指針,該指
針將來(lái)指向一塊堆空間,內(nèi)部包含了如下字段:
- 空間總大小
- 字符串有效長(zhǎng)度
- 引用計(jì)數(shù)
struct _Rep_base
{size_type _M_length;size_type _M_capacity;_Atomic_word _M_refcount;
};
- 指向堆空間的指針,用來(lái)存儲(chǔ)字符串。