string_view 是C++17所提供的用于处理只读字符串的轻量对象。这里后缀 view 的意思是只读的视图。
- 通过调用 string_view 构造器可将字符串转换为 string_view 对象。
string 可隐式转换为 string_view。 - string_view 是只读的轻量对象,它对所指向的字符串没有所有权。
- string_view通常用于函数参数类型,可用来取代 const char* 和 const string&。
- string_view 代替 const string&,可以避免不必要的内存分配。
- string_view的成员函数即对外接口与 string 相类似,但只包含读取字符串内容的部分。string_view::substr()的返回值类型是string_view,不产生新的字符串,不会进行内存分配。string::substr()的返回值类型是string,产生新的字符串,会进行内存分配。
- string_view字面量的后缀是 sv。(string字面量的后缀是 s)。
使用 std::string_view 可以避免字符串数据的复制,从而提高程序效率。C++ 中的 string 类型在堆上存放自己的字符串数据,所以当你处理 string 类型的时候,很容易就会产生(堆)内存分配。在实际应用中,字符串操作会很频繁,频繁的内存分配会拖慢程序的运行速度。
示例代码:
- NO string_view
#include <iostream>
#include <string>
#include <string_view>
static uint32_t s_AllocCount = 0;
//操作符重载跟踪内存分配次数和分配大小
void* operator new(size_t size)
{
s_AllocCount++;
std::cout << "Allocating " << size << " bytes" << std::endl;
return malloc(size);
}
//std::string和c++17 std::string_view比较
//#define start_view 1
#if start_view
void PrintName(std::string_view name)
{
std::cout << name << std::endl;
}
#else
void PrintName(const std::string& name)
{
std::cout << name << std::endl;
}
#endif
int main()
{
std::string name = "Hello world";
#if start_view
std::string_view fistName(name.c_str(), 5);
std::string_view lastName(name.c_str() + 6, 5);
#else
std::string fistName = name.substr(0, 5);
std::string lastName = name.substr(6, 10);
#endif
PrintName(name);
PrintName(fistName);
PrintName(lastName);
std::cout << s_AllocCount << " allocations" << std::endl;
std::cin.get();
}
使用一个简单得PrintName函数分别打印整个name和第一个单词和第二个单词,在没有使用string_view时,运行结果:
Allocating 8 bytes
Allocating 8 bytes
Allocating 8 bytes
Hello world
Hello
world
3 allocations
这里打印了三次进行了三次内存分配,那么1000次,10000次,则会严重影响程序运行效率。
- string_view
#include <iostream>
#include <string>
#include <string_view>
static uint32_t s_AllocCount = 0;
//操作符重载跟踪内存分配次数和分配大小
void* operator new(size_t size)
{
s_AllocCount++;
std::cout << "Allocating " << size << " bytes" << std::endl;
return malloc(size);
}
//std::string和c++17 std::string_view比较
#define start_view 1
#if start_view
void PrintName(std::string_view name)
{
std::cout << name << std::endl;
}
#else
void PrintName(const std::string& name)
{
std::cout << name << std::endl;
}
#endif
int main()
{
std::string name = "Hello world";
#if start_view
std::string_view fistName(name.c_str(), 5);
std::string_view lastName(name.c_str() + 6, 5);
#else
std::string fistName = name.substr(0, 5);
std::string lastName = name.substr(6, 10);
#endif
PrintName(name);
PrintName(fistName);
PrintName(lastName);
std::cout << s_AllocCount << " allocations" << std::endl;
std::cin.get();
}
输出结果:
Allocating 8 bytes
Hello world
Hello
world
1 allocations
在这里仅仅经行了一次内存分配,发生在std::string name = "Hello world";
。
实际上在不同的编译器中结果似乎是不同的,在gcc中打出如上代码后并没有获得一样的结果,在gcc中PrintName函数中的引用并没有向堆申请内存,而vc编译器却向堆申请了内存。
这是因为:gcc的string默认大小是32个字节,字符串小于等于15直接保存在栈上,超过之后才会使用new分配。
额外的
让std:string 更快的方法是不用它,std::string name = "Hello world";
name是一个静态字符串,可以直接使用const char* name = "Hello world"
,然后修改
std::string_view fistName(name.c_str(), 5);
std::string_view lastName(name.c_str() + 6, 5);
为
std::string_view fistName(name, 5);
std::string_view lastName(name + 6, 5);
可以得到0分配。