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++
如何使用C++标准库中的std::function来简化回调函数的使用
如何使用C++标准库中的std::function来简化回调函数的使用
15 6
|
23小时前
|
C++
在C++中,通常如何确保在使用std::mutex后能自动释放锁
在C++中,通常如何确保在使用std::mutex后能自动释放锁
8 2
|
6天前
|
C++
C++ string中的函数和常用用法
C++ 中string中的函数和常用用法
15 4
|
22天前
|
C语言 C++
【C++】string模拟实现(下)
本文档介绍了自定义`string`类的一些关键功能实现,包括`reserve()`用于内存管理,`push_back()`和`append()`添加字符或字符串,运算符`+=`的重载,以及`insert()`, `erase()`进行插入和删除操作。此外,还涵盖了`find()`查找函数,字符串的比较运算符重载,`substr()`获取子串,`clear()`清除内容,以及流插入和提取操作。常量`npos`用于表示未找到的标记。文档以代码示例和运行结果展示各功能的使用。
|
22天前
|
编译器 程序员 C语言
【C++】string模拟实现
这篇博客探讨了自定义实现C++ `string` 类的关键功能,包括构造、拷贝构造、赋值运算符重载及析构函数。作者强调了理解并实现这些功能对于面试的重要性。博客介绍了`string` 类的头文件`string.h`,其中定义了迭代器、基本成员函数如`swap()`、`size()`、`c_str()`等,并提到了深拷贝概念。此外,还展示了构造函数、析构函数和赋值运算符的实现,以及迭代器的定义与使用。博客还包括对C语言字符串函数的引用,以辅助读者理解实现细节。
|
22天前
|
编译器 C++
【C++】string类的使用④(字符串操作String operations )
这篇博客探讨了C++ STL中`std::string`的几个关键操作,如`c_str()`和`data()`,它们分别返回指向字符串的const char*指针,前者保证以&#39;\0&#39;结尾,后者不保证。`get_allocator()`返回内存分配器,通常不直接使用。`copy()`函数用于将字符串部分复制到字符数组,不添加&#39;\0&#39;。`find()`和`rfind()`用于向前和向后搜索子串或字符。`npos`是string类中的一个常量,表示找不到匹配项时的返回值。博客通过实例展示了这些函数的用法。
|
22天前
|
存储 C++
【C++】string类的使用③(非成员函数重载Non-member function overloads)
这篇文章探讨了C++中`std::string`的`replace`和`swap`函数以及非成员函数重载。`replace`提供了多种方式替换字符串中的部分内容,包括使用字符串、子串、字符、字符数组和填充字符。`swap`函数用于交换两个`string`对象的内容,成员函数版本效率更高。非成员函数重载包括`operator+`实现字符串连接,关系运算符(如`==`, `&lt;`等)用于比较字符串,以及`swap`非成员函数。此外,还介绍了`getline`函数,用于按指定分隔符从输入流中读取字符串。文章强调了非成员函数在特定情况下的作用,并给出了多个示例代码。
|
22天前
|
C++
【C++】string类的使用④(常量成员Member constants)
C++ `std::string` 的 `find_first_of`, `find_last_of`, `find_first_not_of`, `find_last_not_of` 函数分别用于从不同方向查找目标字符或子串。它们都返回匹配位置,未找到则返回 `npos`。`substr` 用于提取子字符串,`compare` 则提供更灵活的字符串比较。`npos` 是一个表示最大值的常量,用于标记未找到匹配的情况。示例代码展示了这些函数的实际应用,如替换元音、分割路径、查找非字母字符等。
|
22天前
|
C++
C++】string类的使用③(修改器Modifiers)
这篇博客探讨了C++ STL中`string`类的修改器和非成员函数重载。文章介绍了`operator+=`用于在字符串末尾追加内容,并展示了不同重载形式。`append`函数提供了更多追加选项,包括子串、字符数组、单个字符等。`push_back`和`pop_back`分别用于在末尾添加和移除一个字符。`assign`用于替换字符串内容,而`insert`允许在任意位置插入字符串或字符。最后,`erase`函数用于删除字符串中的部分内容。每个函数都配以代码示例和说明。
|
22天前
|
安全 编译器 C++
【C++】string类的使用②(元素获取Element access)
```markdown 探索C++ `string`方法:`clear()`保持容量不变使字符串变空;`empty()`检查长度是否为0;C++11的`shrink_to_fit()`尝试减少容量。`operator[]`和`at()`安全访问元素,越界时`at()`抛异常。`back()`和`front()`分别访问首尾元素。了解这些,轻松操作字符串!💡 ```