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 << 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展⽰出来的数据, 这不是很⽭盾吗?
因篇幅问题不能全部显示,请点此查看更多更全内容