1. 程式人生 > 程式設計 >C++17 使用 std::string_view避免字串拷貝優化程式效能

C++17 使用 std::string_view避免字串拷貝優化程式效能

C++中std::string是日常Coding中經常使用的一個類,使用起來非常方便,但是也存在一些弊端。

如下程式碼,引數傳遞的過程發生了記憶體分配(Memory Allocation)和記憶體拷貝。

void fun(const std::string& s) {
  std::cout << s << std::endl;
}

const char* ch = "hello world";

// bad way,expensive if the string is long
fun(ch); 

再看下面的常用的字串擷取實現:

// very long string
std::string str = "lllloooonnnngggg sssstttrrriiinnnggg"; 

// bad way,expensive if the string is long
std::cout << str.substr(15,10) << '\n';

為了進一步的壓榨程式的效能,需要移除掉這些昂貴的字串記憶體分配和拷貝操作。C++17中提供了std::string_view可以幫助我們實現這一功能,該類並不持有字串的拷貝,而是與源字串共享其記憶體空間。

string_view建構函式

constexpr basic_string_view() noexcept; (since C++17)
constexpr basic_string_view(const basic_string_view& other) noexcept = default; (since C++17)
constexpr basic_string_view(const CharT* s,size_type count);(since C++17)
constexpr basic_string_view(const CharT* s); (since C++17)
template<class It,class End>

雖然沒有定義引數為std::string的std::string_view函式,下面的程式碼仍然可以通過編譯。

std::string str("hello string view!");
std::string_view sview(str);

因為std::string類過載了std::string到std::string_view的轉換操作符。

operator basic_string_view<charT,traits>() const noexcept;

std::string_view避免記憶體拷貝

有了string_view,我們就可以很好的解決文章開頭提到的問題。

void fun(const std::string_view& s) {
  std::cout << s << std::endl;
}

const char* ch = "hello world,char";
fun(ch); 

const std::string str = "hello world,string";
fun(str);

fun({ch,5});

有了std::string_view,函式引數不管是字串陣列指標,還是std::string,都不需要拷貝記憶體源字串。

// very long string
std::string str = "lllloooonnnngggg sssstttrrriiinnnggg";

//Good way - No copies are created!
std::string_view view = str;

// string_view::substr returns a new string_view
std::cout << view.substr(15,10) << '\n';

同理,字串的substr()函式也不需要再進行拷貝了,這對於非常長的字串,可以有效的提升程式的效能表現。

std::string_view注意事項

由於std::string_view並不持有字串的記憶體,所以它的生命週期一定要比源字串的生命週期長。一些典型的生命週期管理錯誤的例子:

std::string_view sv = std::string("hello world");

std::string_view fun() {
 std::string str("hello world");
 return std::string_view(str);
}

std::string_view的結尾並沒有\0結束符,所以在輸出std::string_view的時候要注意邊界,如下程式碼:

#include <iostream>
#include <vector>
#include <string>
#include <string_view>

int main() {
  const char* ch = "hello world";
  
  std::string_view sv(ch,2);
  
  std::cout << sv << std::endl;
  
  std::cout << sv.data() << std::endl;
  
  return 0;
}

程式輸出:

he
hello world

std::cout << sv.data() << std::endl; 這行程式碼輸出了hello world,這是因為sv仍然指向源字串記憶體,呼叫sv.data()列印時,仍然沿用了C++對於字串的約定,直到遇到結束符\0時,列印才結束,這時候就輸出了完整的源字串內容。當然這裡明顯是不符合預期的,尤其是std::string_view指向的字串沒有\0結束符的時候,程式很容易出現潛在的記憶體問題。所以此處一定要小心。

參考材料

https://riptutorial.com/cplusplus/example/6571/using-the-std–string-view-class

https://en.cppreference.com/w/cpp/string/basic_string_view

https://segmentfault.com/a/1190000018387368

以上就是C++17 使用 std::string_view避免字串拷貝優化程式效能的詳細內容,更多關於c++17 std::string_view的資料請關注我們其它相關文章!