string_View理解与用法(一)
什么是string_view
當(dāng)你創(chuàng)建一個(gè)將(常量)字符串作為參數(shù)的函數(shù)時(shí),你有四個(gè)選擇,你可能知道兩個(gè),但不知道另外兩個(gè):
void TakesCharStar(const char* s); // C convention void TakesString(const string& s); // Old Standard C++ convention void TakesStringView(absl::string_view s); // Abseil C++ convention void TakesStringView(std::string_view s); // C++17 C++ convention當(dāng)調(diào)用者已經(jīng)有已提供的格式的字符串時(shí)即提供的字符串類(lèi)型完全匹配時(shí)(對(duì)于第1個(gè)函數(shù),調(diào)用者提供的字符串類(lèi)型為:const char*,如:const char *p = "test";? 對(duì)于第2個(gè)函數(shù),調(diào)用者提供的字符串類(lèi)型為const string,如: const string p = "test"; ),前兩者各自對(duì)應(yīng)的方法最有效,但是當(dāng)需要進(jìn)行轉(zhuǎn)換(如從const char *到string 或 string到char *)時(shí)發(fā)生什么呢?
調(diào)用者需要將字符串轉(zhuǎn)換為const char *時(shí),需要用(高效但不方便)c_str()函數(shù):
void AlreadyHasString(const string& s) {TakesCharStar(s.c_str()); // explicit conversion }調(diào)用者需要將const char *轉(zhuǎn)換為字符串時(shí),不需要做任何其他操作(這是好消息);但是將創(chuàng)建臨時(shí)字符串(方便但效率低),并復(fù)制該字符串的內(nèi)容(這是壞消息)。
string有什么缺點(diǎn)?
本節(jié)內(nèi)容主要摘自博客【現(xiàn)代C++】性能控的工具箱之string_view。
在數(shù)據(jù)傳遞中減少拷貝是提高性能的最常用辦法。在C中指針是完成這一目的的標(biāo)準(zhǔn)數(shù)據(jù)結(jié)構(gòu),而在C++中引入了安全性更高的引用類(lèi)型。所以在C++中若傳遞的數(shù)據(jù)僅僅可讀,const string&成了C++天然的方式。但這并非完美,從實(shí)踐上來(lái)看,它至少有以下幾方面問(wèn)題:
- 字符串字面值、字符數(shù)組、字符串指針的傳遞依然要數(shù)據(jù)拷貝
這三類(lèi)低級(jí)數(shù)據(jù)類(lèi)型與string類(lèi)型不同,傳入時(shí)編譯器要做隱式轉(zhuǎn)換,即需要拷貝這些數(shù)據(jù)生成string臨時(shí)對(duì)象。const string&指向的實(shí)際上是這個(gè)臨時(shí)對(duì)象。通常字符串字面值較小,性能損失可以忽略不計(jì);但字符串指針和字符數(shù)組某些情況下可能會(huì)比較大(比如讀取文件的內(nèi)容),此時(shí)會(huì)引起頻繁的內(nèi)存分配和數(shù)據(jù)拷貝,影響程序性能。
- substr O(n)復(fù)雜度
substr是個(gè)常用的函數(shù),好在std::string提供了這個(gè)函數(shù),美中不足的時(shí)每次都要返回一個(gè)新生成的子串,很容易引起性能熱點(diǎn)。實(shí)際上我們本意不是要改變?cè)址?#xff0c;為什么不在原字符串基礎(chǔ)上返回呢?
怎么辦
在C++17中引入了string_view,能很好的解決以上兩個(gè)問(wèn)題。
std::string_view是C++ 17標(biāo)準(zhǔn)中新加入的類(lèi),正如其名,它提供一個(gè)字符串的視圖,即可以通過(guò)這個(gè)類(lèi)以各種方法“觀測(cè)”字符串,但不允許修改字符串。由于它只讀的特性,它并不真正持有這個(gè)字符串的拷貝,而是與相對(duì)應(yīng)的字符串共享這一空間。即——構(gòu)造時(shí)不發(fā)生字符串的復(fù)制(具體請(qǐng)參考《詳解C++17下的string_view》)。同時(shí),你也可以自由的移動(dòng)這個(gè)視圖,移動(dòng)視圖并不會(huì)移動(dòng)原定的字符串。
- 通過(guò)調(diào)用 string_view 構(gòu)造器可將字符串轉(zhuǎn)換為 string_view 對(duì)象。string 可隱式轉(zhuǎn)換為 string_view。
- string_view 是只讀的輕量對(duì)象,它對(duì)所指向的字符串沒(méi)有所有權(quán)。
- string_view通常用于函數(shù)參數(shù)類(lèi)型,可用來(lái)取代 const char* 和 const string&。string_view 代替 const string&,可以避免不必要的內(nèi)存分配。
- string_view的成員函數(shù)即對(duì)外接口與 string 相類(lèi)似,但只包含讀取字符串內(nèi)容的部分。
string_view::substr()的返回值類(lèi)型是string_view,不產(chǎn)生新的字符串,不會(huì)進(jìn)行內(nèi)存分配。string::substr()的返回值類(lèi)型是string,產(chǎn)生新的字符串,會(huì)進(jìn)行內(nèi)存分配。 - string_view字面量的后綴是 sv。(string字面量的后綴是 s)
輸出:
s1: 3 "abc" s2: 8 "abc^@^@def"以上例子能很好看清二者的語(yǔ)義區(qū)別,\0對(duì)于字符串而言,有其特殊的意義,即表示字符串的結(jié)束,字符串視圖根本不care,它關(guān)心實(shí)際的字符個(gè)數(shù)。
Google首選通過(guò)stringview接受這樣的字符串參數(shù)。這是C++17的“pre-adopted”類(lèi)型,在C++17的構(gòu)建中,您應(yīng)該使用std::string_view,在任何不依賴(lài)C++17的代碼中,您應(yīng)該使用absl::string_view(Abseil是Google開(kāi)源的C++庫(kù))。
string_view類(lèi)的實(shí)例可以看作是現(xiàn)有字符串緩沖區(qū)的“視圖”。具體來(lái)說(shuō),string_view僅由一個(gè)指針和一個(gè)長(zhǎng)度組成,用于標(biāo)記不是string _view擁有且不能被該視圖修改的字符串?dāng)?shù)據(jù)部分。所以,復(fù)制string_view是一項(xiàng)淺層的操作:不復(fù)制任何字符串?dāng)?shù)據(jù)。
string_view有來(lái)自const char * 和 const string&的隱式轉(zhuǎn)換構(gòu)造函數(shù),并且由于string_view不拷貝,因此進(jìn)行淺拷貝不產(chǎn)生O(n)內(nèi)存損失。在傳遞cosnt string&的情況下,構(gòu)造函數(shù)在O(1)時(shí)間進(jìn)行。在傳遞const char*的情況下,構(gòu)造函數(shù)會(huì)自動(dòng)調(diào)用strlen()(或者你可以使用具有兩個(gè)參數(shù)的string_view構(gòu)造函數(shù))。
void AlreadyHasString(const string& s) {TakesStringView(s); // no explicit conversion; convenient! }void AlreadyHasCharStar(const char* s) {TakesStringView(s); // no copy; efficient! }因?yàn)閟tring_view不擁有數(shù)據(jù),所以string_view所指的任何字符串必須具有已知的生命周期,并且必須比string_view本身生命周期更長(zhǎng)。這意味著使用string_view進(jìn)行存儲(chǔ)通常是有問(wèn)題的:你需要一些證據(jù)證明基礎(chǔ)數(shù)據(jù)的生命周期將超過(guò)string_view。
如果你的API僅需在一次調(diào)用中引用字符串?dāng)?shù)據(jù),而無(wú)需修改數(shù)據(jù),則接受string_view就足夠了。如果以后需要引用數(shù)據(jù)或需要修改數(shù)據(jù),則可以使用string(my_string_view)顯式轉(zhuǎn)換為C ++字符串對(duì)象。
將string_view添加到現(xiàn)有代碼庫(kù)中并非總是正確的答案:更改參數(shù)以通過(guò)string_view傳遞可能效率不高,如果這些參數(shù)隨后傳遞給需要字符串或以NUL終止的const char *的函數(shù)。最好從實(shí)用程序代碼開(kāi)始向上使用string_view,或者在啟動(dòng)新項(xiàng)目時(shí)保持完全一致。
其他事項(xiàng)
- 與其他字符串類(lèi)型不同,你應(yīng)該按值傳遞string_view,就像int或double一樣,因?yàn)閟tring_view是一個(gè)很小的值。
- string_view不一定是NUL終止的。因此,編寫(xiě)以下內(nèi)容并不安全:
以下這篇文章很好說(shuō)明了上例的不安全:
《C++ string_view 的坑》
但是,下面是好的代碼:
printf("%.*s\n", static_cast<int>(sv.size()), sv.data());- 你可以輸出string_view,就像輸出字符串或const char*一樣:
大多數(shù)情況下,你可以將接受const string&或NUL終止的const char*的現(xiàn)有例程安全的轉(zhuǎn)換為string_view。在執(zhí)行此操作時(shí)遇到的唯一危險(xiǎn)是,如果已獲取函數(shù)的地址,則將導(dǎo)致編譯中斷,因?yàn)樯傻暮瘮?shù)指針類(lèi)型將有所不同。
總結(jié)
以上是生活随笔為你收集整理的string_View理解与用法(一)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 开发常用镜像资源替换为国内开源镜像(yu
- 下一篇: 关于项目延迟交付