一、标准库字符串处理

C和C++的一个很不一样的区别就是对字符串的处理,在c++的标准库里提供了一个std::string的字符串操作类。这使得c++对字符串的操作从某种程度上摆脱了原始指针的操作。从这个角度来说,对c++小白来说,肯定是利好的。但麻烦就在于c++强大的灵活性,导致在处理字符串时,效率会由于不同的应用编码导致差异性很大;同时,一不小心就有可能对原始字符串进行了修改,导致异常的发生。而这些,都不是一个普通菜鸟能够解决的。
当然,如果从一个完美的角度来看待这个问题,基本是无解的,毕竟c++的整体的设计目标在那儿。但是在一些细节上不断完善,或者在某一个方面上进一步的优化,c++是可以办到的,这就是今天要分析的c++17提供的std::string_view这个类。

二、std::string_view

这个类,有点类似于Golang语言中的切片Slice。这就意味着std::string_view本身并不拥有内存本身,它只是一个View,一个窗口,观看内存的窗口。这样理解可能就比较形象了。搞出这个类的目的非常简单,就是因为c++在处理字符串时,经常会对字符串进行显示的Copy或者隐式的拷贝,显示的还容易优化,隐式的就非常考验是不是老鸟了。但是如果有这么一个类,只是用来对内存字符串进行展示操作,也就是说不可能有内存本身的复制操作,直接不就优化到最底层了。这也是std::string_view类的思想。
但是有一得则必有一失,使用这个类需要有两点注意:
1、既然它是内存的视图、观察者,那么,就意味着字符串内存的生命周期(Runtime为动态)或者说作用域(Compile为静态)一定要大于std::string_view,否则可能有未知的后果。
2、std::string_view不像c/c++有一样有一个’\0’的终结符。一定要注意这点,这意味着,这个显示的长度内容,需要手动控制。
在下面的代码分析中,会对这两点进行一个说明。

三、源码分析

看一下在c++中的定义:

template<class CharT,class Traits = std::char_traits<CharT>
> class basic_string_view; Type  Definition
std::string_view (C++17)  std::basic_string_view<char>
std::wstring_view (C++17) std::basic_string_view<wchar_t>
std::u8string_view (C++20)    std::basic_string_view<char8_t>
std::u16string_view (C++17)   std::basic_string_view<char16_t>
std::u32string_view (C++17)   std::basic_string_view<char32_t>

这里对基本的代码进行一下分析:

template<class _Elem,class _Traits>class basic_string_view{    // wrapper for any kind of contiguous character buffer
public:static_assert(is_same_v<_Elem, typename _Traits::char_type>, "Bad char_traits for basic_string_view; ""N4659 24.4.2 [string.view.template]/1 \"the type traits::char_type shall name the same type as charT.\"");using traits_type            = _Traits;using value_type             = _Elem;using pointer                = _Elem *;using const_pointer          = const _Elem *;using reference              = _Elem&;using const_reference        = const _Elem&;using const_iterator         = _String_view_iterator<_Traits>;using iterator               = const_iterator;using const_reverse_iterator = _STD reverse_iterator<const_iterator>;using reverse_iterator       = const_reverse_iterator;using size_type              = size_t;using difference_type        = ptrdiff_t;static constexpr auto npos{static_cast<size_type>(-1)};constexpr basic_string_view() noexcept: _Mydata(),_Mysize(0){  // construct empty basic_string_view}constexpr basic_string_view(const basic_string_view&) noexcept = default;constexpr basic_string_view& operator=(const basic_string_view&) noexcept = default;   /* implicit */ constexpr basic_string_view(_In_z_ const const_pointer _Ntcts) noexcept // strengthened: _Mydata(_Ntcts),_Mysize(_Traits::length(_Ntcts)){   // construct basic_string_view around a null-terminated character-type sequence}constexpr basic_string_view(_In_reads_(_Count) const const_pointer _Cts, const size_type _Count)noexcept // strengthened: _Mydata(_Cts),_Mysize(_Count){    // construct basic_string_view around a character-type sequence with explicit size
#if _ITERATOR_DEBUG_LEVEL >= 1_STL_VERIFY(_Count == 0 || _Cts, "non-zero size null string_view");
#endif /* _ITERATOR_DEBUG_LEVEL >= 1 */}_NODISCARD constexpr int compare(_In_z_ const _Elem * const _Ptr) const{    // compare [0, _Mysize) with [_Ptr, <null>)return (compare(basic_string_view(_Ptr)));}_NODISCARD constexpr int compare(const size_type _Off, const size_type _N0,_In_z_ const _Elem * const _Ptr) const{  // compare [_Off, _Off + _N0) with [_Ptr, <null>)return (substr(_Off, _N0).compare(basic_string_view(_Ptr)));}_NODISCARD constexpr int compare(const size_type _Off, const size_type _N0,_In_reads_(_Count) const _Elem * const _Ptr, const size_type _Count) const{ // compare [_Off, _Off + _N0) with [_Ptr, _Ptr + _Count)return (substr(_Off, _N0).compare(basic_string_view(_Ptr, _Count)));}_NODISCARD constexpr size_type find(const basic_string_view _Right, const size_type _Off = 0) const noexcept{   // look for _Right beginning at or after _Offreturn (_Traits_find<_Traits>(_Mydata, _Mysize, _Off, _Right._Mydata, _Right._Mysize));}_NODISCARD constexpr size_type find(const _Elem _Ch, const size_type _Off = 0) const noexcept{  // look for _Ch at or after _Offreturn (_Traits_find_ch<_Traits>(_Mydata, _Mysize, _Off, _Ch));}_NODISCARD constexpr size_type find(_In_reads_(_Count) const _Elem * const _Ptr, const size_type _Off,const size_type _Count) const noexcept // strengthened{ // look for [_Ptr, _Ptr + _Count) beginning at or after _Offreturn (_Traits_find<_Traits>(_Mydata, _Mysize, _Off, _Ptr, _Count));}
......
} template<class _Traits>constexpr int _Traits_compare(_In_reads_(_Left_size) const _Traits_ptr_t<_Traits> _Left, const size_t _Left_size,_In_reads_(_Right_size) const _Traits_ptr_t<_Traits> _Right, const size_t _Right_size) noexcept{    // compare [_Left, _Left + _Left_size) to [_Right, _Right + _Right_size) using _Traitsconst int _Ans = _Traits::compare(_Left, _Right, _Min_value(_Left_size, _Right_size));if (_Ans != 0){return (_Ans);}if (_Left_size < _Right_size){return (-1);}if (_Left_size > _Right_size){return (1);}return (0);}template<class _Traits>constexpr size_t _Traits_find(_In_reads_(_Hay_size) const _Traits_ptr_t<_Traits> _Haystack, const size_t _Hay_size, const size_t _Start_at,_In_reads_(_Needle_size) const _Traits_ptr_t<_Traits> _Needle, const size_t _Needle_size) noexcept{    // search [_Haystack, _Haystack + _Hay_size) for [_Needle, _Needle + _Needle_size), at/after _Start_atif (_Needle_size > _Hay_size || _Start_at > _Hay_size - _Needle_size){    // xpos cannot exist, report failure// N4659 24.3.2.7.2 [string.find]/1 says:// 1. _Start_at <= xpos// 2. xpos + _Needle_size <= _Hay_size;// therefore:// 3. _Needle_size <= _Hay_size (by 2) (checked above)// 4. _Start_at + _Needle_size <= _Hay_size (substitute 1 into 2)// 5. _Start_at <= _Hay_size - _Needle_size (4, move _Needle_size to other side) (also checked above)return (static_cast<size_t>(-1));}if (_Needle_size == 0){ // empty string always matches if xpos is possiblereturn (_Start_at);}const auto _Possible_matches_end = _Haystack + (_Hay_size - _Needle_size) + 1;for (auto _Match_try = _Haystack + _Start_at; ; ++_Match_try){_Match_try = _Traits::find(_Match_try, static_cast<size_t>(_Possible_matches_end - _Match_try), *_Needle);if (!_Match_try){ // didn't find first character; report failurereturn (static_cast<size_t>(-1));}if (_Traits::compare(_Match_try, _Needle, _Needle_size) == 0){ // found matchreturn (static_cast<size_t>(_Match_try - _Haystack));}}}

其实你看它的底层代码实现,其实也没有什么,有一点经验就可以看得比较清楚。又回复到了最初的算法和数据结构,看来还是要把数据结构算法搞的扎实一些。

四、实例

看一下几个相关的实例(cppreference.com):


#include <string_view>int main()
{using namespace std::literals;constexpr auto str{" long long int;"sv};static_assert(1 == str.find("long"sv)            && "<- find(v , pos = 0)" &&6 == str.find("long"sv, 2)         && "<- find(v , pos = 2)" &&0 == str.find(' ')                 && "<- find(ch, pos = 0)" &&2 == str.find('o', 1)              && "<- find(ch, pos = 1)" &&2 == str.find("on")                && "<- find(s , pos = 0)" &&6 == str.find("long double", 5, 4) && "<- find(s , pos = 5, count = 4)");static_assert(str.npos == str.find("float"));
}

再看一个生命周期和终结符的示例:

#include <iostream>
#include <vector>
#include <string>
#include <string_view>std::string_view Test()
{std::string s("sssss  dd");return std::string_view(s);
}
void TestView()
{std::string_view s = Test();std::cout<<s<<std::endl;
}int main() {const char* cStr = "My Test";std::string_view sView(cStr, 3);std::cout << sView << std::endl;std::cout << sView.data() << std::endl;TestView();return 0;
}

运行结果:

My
My Test
.......#@    //说明,此处是未知的代码,无法拷贝上来

在《c++17入门经典》这本书里提到过std::string_view在处理字符串常量时,const仍然无法阻止字符串的隐式复制的情形,其实这也是std::string_view的一个重要的应用之处。其它如传参和返回值中都有这种情形,可以认真的思考一下。

五、总结

在网上听人说过,任何进步都是站在巨人的肩膀的前进的。换句话说,进步甚至技术暴发都不是凭空出现的,都是在前面的坑的基础上不断的总结和完善,堆积到一定程度,量变到质变。c++也是如此,蜇伏了多年的c++在其它语言的快速发展影响下,也不断的在吸收自己和别人的先进经验,不断的推陈出新,这就是c++的生命力所在。
梧桐一叶落,而知天下秋。如是而已!
努力要从今日始!归来的少年!

跟我学c++中级篇——STL字符串之std::string_view相关推荐

  1. 跟我学c++中级篇——类型擦除

    一.类型擦除 很多人一直都认为,类型擦除是一些高级语言(如Java)才具有的,其实在c++中也可以实现类型擦除.那么什么是类型擦除呢?我们都知道,C/c++是一门强类型语言,也就是说,编译器必须知道数 ...

  2. 跟我学c++中级篇——concepts的几个应用

    一.concepts的入门应用 concepts的应用是一个非常必要的问题.它对于模板在实际编程中的友好性有着至关重要的作用.先从最简单的一个示例说起: struct PlusSum{int d_ = ...

  3. 『中级篇』docker导学(一)

    原创文章,欢迎转载.转载请注明:转载自IT人故事会,谢谢! 原文链接地址:『中级篇』docker导学(一) 这两年容器技术及其相关工具,平台异常火爆.在各大技术论坛或云计算峰会议题中,都会占很大比重, ...

  4. Java工程师学习指南 中级篇

    Java工程师学习指南 中级篇 最近有很多小伙伴来问我,Java小白如何入门,如何安排学习路线,每一步应该怎么走比较好.原本我以为之前的几篇文章已经可以解决大家的问题了,其实不然,因为我写的文章都是站 ...

  5. 视频教程-C# For Unity系列之中级篇-Unity3D

    C# For Unity系列之中级篇 二十多年的软件开发与教学经验IT技术布道者,资深软件工程师.具备深厚编程语言经验,在国内上市企业做项目经理.研发经理,熟悉企业大型软件运作管理过程.软件架构设计理 ...

  6. Java工程师学习指南(中级篇)

    Java工程师学习指南 中级篇 最近有很多小伙伴来问我,Java小白如何入门,如何安排学习路线,每一步应该怎么走比较好.原本我以为之前的几篇文章已经可以解决大家的问题了,其实不然,因为我写的文章都是站 ...

  7. 『中级篇』docker容器安装wordpress(37)

    原创文章,欢迎转载.转载请注明:转载自IT人故事会,谢谢! 原文链接地址:『中级篇』docker容器安装wordpress(37) 第一节的时候我就部署过wordpress,可能很多老铁一头雾水不知道 ...

  8. 学Redis这篇就够了!

    作者:王爷科技 https://www.toutiao.com/i6713520017595433485 Redis 简介 & 优势 Redis 数据类型 发布订阅 订阅者的客户端显示如下 事 ...

  9. Android中级篇之百度地图SDK v3.5.0-一步一步带你仿各大主流APP地图定位移动选址功能

    from: http://blog.csdn.net/y1scp/article/details/49095729 定位+移动选址 百学须先立志-学前须知: 我们经常在各大主流APP上要求被写上地址, ...

最新文章

  1. app手机端连接tomcat电脑端服务器
  2. tomcat虚拟目录和虚拟主机等相关配置
  3. 深入理解Spark 2.1 Core (三):任务调度器的原理与源码分析
  4. QQuickWidget + QML编程实现酷炫动态动画效果
  5. 编程之美2——N!的二进制表示中最低位1的位置
  6. GridView网格控件
  7. 3. 机器学习中为什么需要梯度下降?梯度下降算法缺点?_一起学习西瓜书2
  8. 步步为营VS 2008 + .NET 3.5(2) - VS 2008新特性之JavaScript Intellisense and Debugging
  9. 计算机二级考试c语言 上机,计算机等级考试二级C语言上机题[2]
  10. 【线性代数】P3 拉普拉斯定理
  11. fastjson笔记
  12. matlab中3乘4魔方阵,小代码3 魔方矩阵
  13. 阿里云的maven仓库配置
  14. div中加入span右对齐后出现换行显示的两种解决办法(转)
  15. 判断输入的邮箱格式是否正确
  16. 职教mooc计算机组装与维护课程网课答案,2020-网课答案-高职心理辅导与教育-中国大学mooc...
  17. python疫情监控(爬虫+可视化)
  18. 计算机键盘标注,电脑键盘上怎么打√和*两个符号的方法
  19. windows10安装虚拟机详细教程
  20. 写给还在迷茫中的朋友,一名6年程序员的工作感悟!!!

热门文章

  1. discus mysql 性能_百万纪录级mysql数据库以及discuz!优化
  2. 悲壮!微软钦定的13部 Windows Phone 遗老
  3. 辛巴学院-Unity-剑英陪你零基础学c#系列(一)Hello World
  4. 慧算账V2.0版发布,互联网记账再升级
  5. Centos7安装trash-cli
  6. java计算机毕业设计O2O生鲜果蔬电商设计与实现源码+数据库+系统+lw文档
  7. CIO40知识星球—IT人之兄弟连
  8. REST示例exercise
  9. vue16 自定义键盘属性
  10. 人生的三重境界(山在那?水在那?)