转载
https://www.geeksforgeeks.org/class-stdstring_view-in-cpp-17/
一、背景
在日常C/C 编程中,我们常进行数据的传递操作,比如,将数据传给函数。当数据占用的内存较大时,减少数据的拷贝可以有效提高程序的性能。在C中指针是完成这一目的的标准数据结构,而C 引入了安全性更高的引用类型。所以在C 中若传递的数据仅仅只读,const string&
成了C 的天然的方式。但这并非完美,从实践来看,它至少有以下几方面问题:
- 字符串字面值、字符数组、字符串指针的传递仍要数据拷贝
这三类低级数据类型与
string
类型不同,传入时,编译器需要做隐式转换,即需要拷贝这些数据生成string
临时对象。const string&
指向的实际上是这个临时对象。通常字符串字面值较小,性能损耗可以忽略不计;但字符串指针和字符数组某些情况下可能会比较大(比如读取文件的内容),此时会引起频繁的内存分配和数据拷贝,会严重影响程序的性能。 substr
O(n)复杂度 这是一个特别常用的函数,好在std::string
提供了这个函数,美中不足的是其每次都返回一个新生成的子串,很容易引起性能热点。实际上我们本意并不是要改变原字符串,为什么不在原字符串基础上返回呢?
在C 17中引入了string_view
,能很好的解决以上两个问题。
二、std::string_view
从名字出发,我们可以类比数据库视图,view
表示该类型不会为数据分配存储空间,而且该数据类型只能用来读。该数据类型可通过{数据的起始指针,数据的长度}
两个元素表示,实际上该数据类型的实例不会具体存储原数据,仅仅存储指向的数据的起始指针和长度,所以这个开销是非常小的。
要使用字符串视图,需要引入<string_view>
,下面介绍该数据类型主要的API。这些API基本上都有constexpr
修饰,所以能在编译时很好地处理字符串字面值,从而提高程序效率。
2.1 构造函数
代码语言:javascript复制constexpr string_view() noexcept;
constexpr string_view(const string_view& other) noexcept = default;
constexpr string_view(const CharT* s, size_type count);
constexpr string_view(const CharT* s);
基本上都是自解释的,唯一需要说明的是:为什么我们代码string_view foo(string("abc"))
可以编译通过,但为什么没有对应的构造函数?
实际上这是因为string
类重载了string
到string_view
的转换操作符:
operator std::basic_string_view<CharT, Traits>() const noexcept;
所以,string_view foo(string("abc"))
实际执行了两步操作:
string("abc")
转换为string_view
对象astring_view
使用对象本篇文章从string_view
引入的背景,
2.2 自定义字面量
自定义字面量也是C 17新增的特性,提高了常量的易读。 下面的代码取值cppreference,能很好地说明自定义字面值和字符串语义的差异。
代码语言:javascript复制#include <string_view>
#include <iostream>
int main()
{
using namespace std::literals;
std::string_view s1 = "abc def";
std::string_view s2 = "abc def"sv;
std::cout << "s1: " << s1.size() << " "" << s1 << ""n";
std::cout << "s2: " << s2.size() << " "" << s2 << ""n";
}
输出:
代码语言:javascript复制s1: 3 "abc"
s2: 8 "abc^@^@def"
以上例子能很好看清二者的语义区别,