C++17引入了string_view, 這可是C++程序猿在處理字符串操作的一大福音。因為string_view基本沒有涉及內存的額外分配。
但是在使用的時候,有個地方需要特別注意. 我今天就踩到了這個坑, 特此記錄一下. 問題是這樣的, 我寫了一個函數, 入參是一個以一個點"."為分隔符的字符串. 函數的功能是把字符串的后綴(也就是點后面的部分)替換掉生成一個新的字符串.
代碼如下:
1 string replace_post(string_view src, string_view new_post) 2 { 3 // 找到點的位置 4 auto pos = src.find(".") + 1; 5 // 取出點及點之前的全部字符,string_view的substr會返回一個 6 // string_view對象,所以要取data()賦值給string對象 7 string s1 = src.substr(0, pos).data(); 8 9 // 加上新的后綴 10 return s1 + new_post.data(); 11 } 12 13 14 int main() 15 { 16 string_view sv = "abcdefg.xxx"; 17 string s = replace_post(sv, "yyy"); 18 19 cout << sv << " replaced post by yyy result is:" << s << endl; 20 return 0; 21 }
這段代碼導致我的程序出意料之外的bug, 所以把它記錄在這里.
原本希望的結果是abcdefg.yyy, 而實際結果確是 abcdefg.xxxyyy
src.substr(0, pos).data() 得到的是原始的字符串, 這與預期明顯不符啊. 難道string_view的substr方法有bug?剛開始我懷疑是編譯器的bug,於是我換不同的編譯器進行驗證。
用vc2019, gcc9.1, gcc9.3分別做了驗證, 結果都是一樣的.看來C++標准就是這么定義的了. 那么substr得到是什么,c++文檔里說的一個string_view對象,這個對象里到底有什么數據?
1 string replace_post(string_view src, string_view new_post) 2 { 3 // 找到點的位置 4 auto pos = src.find(".") + 1; 5 // 取出點及點之前的全部字符,string_view的substr會返回一個string_view對象,所以要取data()賦值給string對象 6 string_view sv1 = src.substr(0, pos); 7 string s1 = sv1.data(); 8 cout << "sv1 = " << sv1 << ", s1=" << s1 << endl; 9 10 // 加上新的后綴 11 return s1 + new_post.data(); 12 } 13 14 15 int main() 16 { 17 string_view sv = "abcdefg.xxx"; 18 string s = replace_post(sv, "yyy"); 19 20 cout << sv << " replaced post by yyy result is:" << s << endl; 21 return 0; 22 }
結果如下:
看來, sv1的輸出是正確的. 但是sv1.data()得到確是整個原始字符串, 由此可以推斷string_view內部只是簡單地封裝原始字符串的起始位置和結束位置, 相當於給字符串設置了一個觀察窗口,用戶只能看到通過窗口能看到的那部分數據. data()成員返回的是char*的指針, 是string_view內部字符串的起始位置. 所以其表現再來的行為跟C字符串一樣了, 直到遇到空字符串才結束.
總結,string_view只是某個字符串上建立的一個視圖. 它並不真正持有任何數據,展示給你的不一定是整個字符串,可能只是其中一部分.
但要使用string_view看到的數據又只能通過data(), 從上面例的結果來看: sv1.data() 得到的結果卻不是sv1展示出來的數據, 這不是很矛盾嗎?