C++17新特性之std::string_view

简介: std::string_view系C++17标准发布后新增的内容,类成员变量包含两个部分:字符串指针和字符串长度,相比std::string, std::string_view涵盖了std::string的所有只读接口。如果生成的std::string无需进行修改操作,可以把std::string转换为std::string_view,std::string_view记录了对应的字符串指针和偏移位置,无需管理内存,相对std::string拥有一份字符串拷贝,如字符串查找和拷贝,效率更高。

std::string_view系C++17标准发布后新增的内容,类成员变量包含两个部分:字符串指针和字符串长度,相比std::string, std::string_view涵盖了std::string的所有只读接口。如果生成的std::string无需进行修改操作,可以把std::string转换为std::string_view,std::string_view记录了对应的字符串指针和偏移位置,无需管理内存,相对std::string拥有一份字符串拷贝,如字符串查找和拷贝,效率更高。


废话不多说,上测试代码:

#include "stdafx.h"
#include <iostream>
#include <string>
#include <chrono>
#include <string_view>
class TimerWrapper
{
private:
  std::string str_type_;
  std::chrono::high_resolution_clock::time_point start_time_;
public:
  TimerWrapper(const std::string& str_type) : str_type_(str_type)
  {
    start_time_ = std::chrono::high_resolution_clock::now();
  }
  ~TimerWrapper()
  {
    TimeCost();
  }
  void TimeCost()
  {
    auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - start_time_);
    std::cout << str_type_ << ", costtime: " << ms << std::endl;
  }
};
void TestString(const std::string& str)
{
  std::cout << "string: " << str << std::endl;
  for (int i = 0; i < 1000000; i++)
  {
    std::string sub_str = str.substr(5, 10);
  }
}
void TestStringView(const std::string_view& str_view)
{
  std::cout << "str_view: " << str_view << std::endl;
  for (int i = 0; i < 1000000; i++)
  {
    std::string_view sub_str_view = str_view.substr(5, 10);
  }
}
std::string_view PrintStringView() 
{
  std::string s = "How are you..";
  std::string_view str_view = s;
  return str_view;
}
int main()
{
  std::string str = "abcdefghijklmnopqrtstuvwxyz";
  {
    TimerWrapper timer_wrapper("string");
    TestString(str);
  }
  {
    TimerWrapper timer_wrapper("stringview");
    std::string_view str_view = str;
    TestStringView(str_view);
  }
  std::string_view str_view_str = "testing string_view related..";
  std::string_view str_view_sub_str = str_view_str.substr(5, 10);
  std::cout << "str_view_sub_str: " << str_view_sub_str << "size: " << str_view_sub_str.size() << std::endl;
  std::cout << "str_view_str : " << str_view_str.data() << "size: " << str_view_str.size() << std::endl;
  //std::string strview2strerr = str_view_str;  //报错,不能直接转换
  std::string strview2str = static_cast<std::string>(str_view_str);
  std::cout << "strview2str: " << strview2str << std::endl;
  std::cout << "PrintLocalStringView: " << PrintStringView() << std::endl;
}

先看看执行结果:

4e3ee24ed6d74eda8eddefd8aa3efa87.png

分析下代码,我们做的第一个比较是std::string和std::string_view性能:

void TestString(const std::string& str)
{
  std::cout << "string: " << str << std::endl;
  for (int i = 0; i < 1000000; i++)
  {
    std::string sub_str = str.substr(5, 10);
  }
}
void TestStringView(const std::string_view& str_view)
{
  std::cout << "str_view: " << str_view << std::endl;
  for (int i = 0; i < 1000000; i++)
  {
    std::string_view sub_str_view = str_view.substr(5, 10);
  }
}

为方便数据比较,我们以执行1000000次为例,std::string因为操作过程中,会重新分配内存,生成一个对应的std::string副本,用时1065ms,std::string_view不持有字符串拷贝,与源字符串共享内存空间,其他是视图,避免了std::string频繁的字符串分配和拷贝操作,只用了85ms,效率显而易见。


第二个测试,我们看看string_view的substr操作:

std::string_view str_view_str = "testing string_view related..";
std::string_view str_view_sub_str = str_view_str.substr(5, 10);
std::cout << "str_view_sub_str: " << str_view_sub_str << "size: " << str_view_sub_str.size() << std::endl;
std::cout << "str_view_str : " << str_view_str.data() << "size: " << str_view_str.size() << std::endl;

截取后的字符串和size都是没问题的,这个很容易理解,但是,当我们调用str_view_str.data()时,打印出来的是全字符串。这个是因为str_view_str还是指向的str_view_str,调用str_view_str.data()时,直至遇到结束符\0打印才结束,所以输出的是整个源字符串。


此外,std::string的substr是线性复杂度,依赖于字符串长度, std::string_view的substr是常数复杂度,不依赖于字符串长度,std::string_view的substr的性能优于std::string的substr.


第三个问题,std::string和std::string_view转换问题,调用 string_view构造器可将std::string转换为string_view对象。std::string可隐式转换为 std::string_view,正确的转换可参考下图:

//std::string strview2strerr = str_view_str;  //报错,不能直接转换
std::string strview2str = static_cast<std::string>(str_view_str);
std::cout << "strview2str: " << strview2str << std::endl;

第四个烫烫烫的问题:

std::cout << "PrintLocalStringView: " << PrintStringView() << std::endl;

由于std::string_view并不持有字符串的内存,所以它的生命周期一定要比源字符串的生命周期长,源字符串被消毁,行为未定义。

相关文章
|
1月前
|
编译器 程序员 定位技术
C++ 20新特性之Concepts
在C++ 20之前,我们在编写泛型代码时,模板参数的约束往往通过复杂的SFINAE(Substitution Failure Is Not An Error)策略或繁琐的Traits类来实现。这不仅难以阅读,也非常容易出错,导致很多程序员在提及泛型编程时,总是心有余悸、脊背发凉。 在没有引入Concepts之前,我们只能依靠经验和技巧来解读编译器给出的错误信息,很容易陷入“类型迷路”。这就好比在没有GPS导航的年代,我们依靠复杂的地图和模糊的方向指示去一个陌生的地点,很容易迷路。而Concepts的引入,就像是给C++的模板系统安装了一个GPS导航仪
103 59
|
1月前
|
C++ 容器
|
30天前
|
存储 安全 C++
【C++打怪之路Lv8】-- string类
【C++打怪之路Lv8】-- string类
21 1
|
1月前
|
C++ 容器
|
1月前
|
C++ 容器
|
1月前
|
存储 C++ 容器
|
1月前
|
安全 C语言 C++
【C++篇】探寻C++ STL之美:从string类的基础到高级操作的全面解析
【C++篇】探寻C++ STL之美:从string类的基础到高级操作的全面解析
34 4
|
1月前
|
存储 编译器 程序员
【C++篇】手撕 C++ string 类:从零实现到深入剖析的模拟之路
【C++篇】手撕 C++ string 类:从零实现到深入剖析的模拟之路
63 2
|
26天前
|
C++
C++ 20新特性之结构化绑定
在C++ 20出现之前,当我们需要访问一个结构体或类的多个成员时,通常使用.或->操作符。对于复杂的数据结构,这种访问方式往往会显得冗长,也难以理解。C++ 20中引入的结构化绑定允许我们直接从一个聚合类型(比如:tuple、struct、class等)中提取出多个成员,并为它们分别命名。这一特性大大简化了对复杂数据结构的访问方式,使代码更加清晰、易读。
32 0
|
1月前
|
C语言 C++
深度剖析C++string(中)
深度剖析C++string(中)
47 0