当我们使用string类时,通常会堆内存的分配:
- #include <iostream>
- #include <string>
- using namespace std;
-
- void* operator new(size_t count)
- {
- cout << "new " << count << " bytes" << endl;
- return malloc(count);
- }
-
- int main()
- {
- cout<<"init s1"<<endl;
- string s1 = "123456789";
- cout<<"init s2"<<endl;
- string s2 = "abcdefghijklmnopqrstuvwxyz";
- cout<<"init s3"<<endl;
- string&& s3 = s2.substr(5);
-
- return 0;
- }
-
- 运行程序输出:
- init s1
- init s2
- new 27 bytes
- init s3
- new 22 bytes
可见,当使用string时有时是在堆上分配的内存,有时却不是。
这个是根据字符串的长度来决定的,当字符串的长度(含结束符)小于等于16时,字符串实际上被存储在string对象内部。但是当字符串的长度大于16时,string会在堆上分配内存空间,来存储字符串。
同时成员函数substr也会返回一个string对象,该对象也会依据上述原则来选择存储字符串的方式。
这样管理字符串的方式,如果对于只是读取字符串,那么不免有些效率上的浪费。
虽然我们可以采用传递引用string的方式,减少不必要的内存分配,但这种方式仅对string对象有效,对于char*是失效的。
- #include <iostream>
- #include <string>
- using namespace std;
-
- void* operator new(size_t count)
- {
- cout << "new " << count << " bytes" << endl;
- return malloc(count);
- }
-
- void pStr(int i, const string& s)
- {
- cout<<i<<" pStr s:"<<s<<endl;
- }
-
- int main()
- {
- pStr(1, string("1234567890"));
- pStr(2, "abcdefghijklmnopqrstuvwxyz");
-
- return 0;
- }
-
- 运行程序输出:
- 1 pStr s:1234567890
- new 27 bytes
- 2 pStr s:abcdefghijklmnopqrstuvwxyz
当通过char*的方式传递参数时,会首先根据char*构造出一个string对象来,然后再将其传递个string&。这种方式还是会造成堆内存的分配。
C++17增加了string_view类,用于更高效的读取字符串。
string_view可以认为是string/char*的一个代理类,他可以观察对应的字符串,但不允许对其进行修改。
- #include <iostream>
- #include <string>
- using namespace std;
-
- void* operator new(size_t count)
- {
- cout << "new " << count << " bytes" << endl;
- return malloc(count);
- }
-
- void pStr(int i, string_view s)
- {
- cout<<i<<" pStr s:"<<s<<endl;
- }
-
- int main()
- {
- pStr(1, string("1234567890"));
- pStr(2, "abcdefghijklmnopqrstuvwxyz");
- return 0;
- }
-
- 运行程序输出:
- 1 pStr s:1234567890
- 2 pStr s:abcdefghijklmnopqrstuvwxyz
可见,使用string_view没有堆内存的分配。
对于需要修改string_view的字符串的地方,可以将其转换为string后再修改:
- #include <string>
- #include<iostream>
- using namespace std;
-
- int main()
- {
- string_view a = "12345";
- string b = a.data();
- b += "67890";
- cout << a << endl; //输出:12345
- cout << b << endl; //输出:1234567890
- return 0;
- }
需要注意的是对于其成员函数substr的使用:
- #include <string>
- #include<iostream>
- using namespace std;
-
- int main()
- {
- string_view a = "12345";
- string_view b = a.substr(1, 2);
- cout<<b<<endl; //输出:23
- string c = b.data();
- cout<<c<<endl; //输出:2345
- return 0;
- }
-
- string_view的substr可以类似string.substr一样返回一个新的视图。
- 但是当对这个新的视图获取其data时,实际上是返回的从视图的起点一直到原始字符串结束的所有字符。