Hdi做指数网站,帝国CMS做的淘客网站,wordpress地理定位,成全视频在线时间观看深入研究C17中的std::string_view#xff1a;解锁字符串处理的新境界 一、简介二、std::string_view的基础知识2.1、构造函数2.2、成员函数 三、std::string_view为什么性能高#xff1f;四、std::string_view的使用陷阱五、std::string_view源码解析六、总结 一、简介
C中有… 深入研究C17中的std::string_view解锁字符串处理的新境界 一、简介二、std::string_view的基础知识2.1、构造函数2.2、成员函数 三、std::string_view为什么性能高四、std::string_view的使用陷阱五、std::string_view源码解析六、总结 一、简介
C中有两类字符串即C风格字符串字符串字面值、字符数组、字符串指针和std::string对象两大类。
C风格字符串
#include string.hint main()
{//C风格字符串初始化方式char* arr LionLong;char arr[] LionLong;char arr[] { L, i, o, n, L, o,n, g, \0 }; //结尾必须有\0结束符//C风格字符串函数strlen(arr);strcmp(arr1, arr2);strcat(arr1, arr2);strcpy(arr1, arr2);return 0;
}C std::string对象
#include string//初始化方式
std::string s1;
std::string s2(s1);
std::string s3 s1;
std::string s4(LionLong);
std::string s4 LionLong;
std::string s5 std::string(LionLong);
std::string s6(6, L); //LLLLLL//对象操作
s1.empty();
s1.size();
s[n];
s.substr(3, 5);
当需要将字符串作为参数传递给函数时往往会伴随字符串的拷贝。当数据占用较大内存时减少数据的拷贝显得尤为重要。
在C17之前可以通过C风格字符串指针作为函数形参也可以通过std::string字符串引用类型 作为函数形参。但是这并不完美从实践上看存在以下问题
C风格字符串的传递仍会进行拷贝。字符数组、字符串字面量和字符串指针是可以隐式转换为std::string对象的当函数的形参是std::string而传递的实参是C风格字符串时编译器会做一次隐式转换生成一个临时的std::string对象再让形参指向这个对象。字符串字面值一般较小性能消耗可以忽略不计但是字符数组和字符串指针往往较大频繁的数据拷贝就会造成较大的性能消耗不得不重视。substr()的复杂度是O(N)。std::string提供了一个返回字符串子串的函数但是每次返回的都是一个新的对象也需要进行构造。
那么有没有办法在原始字符串的基础上进行操作呢答案是std::string_view。
在C17中引入的std::string_view是一种轻量级的字符串视图类型类似于Golang的slice。它的出现主要是为了提供一种非拥有性的字符串引用机制用于处理字符串的读取和操作而无需进行内存拷贝或分配新的字符串对象。
std::string_view并不会真正分配存储空间而只是原始数据的一个只读窗口可以认为它是一个内存的观察者。std::string_view的结构非常简单只会保持原始字符串的起始指针以及字符串的长度这个结构不会占用太多内存开销非常小。
std::string_view的出现意义和重要性 减少内存拷贝使用std::string_view可以避免不必要的字符串拷贝操作特别是在函数参数传递和返回值返回时可以显著提高性能和效率。 std::string_view提供了类似std::string的接口可以方便地进行字符串的访问和操作例如查找子串、比较字符串、截取子串等而无需额外的内存分配和释放。现有的基于std::string的代码可以无缝地迁移到使用std::string_view的代码。 std::string_view不仅可以用于处理std::string类型的字符串还可以用于处理其他字符序列包括字符数组、字符指针等。
二、std::string_view的基础知识
std::string_view是对字符串的一种非拥有式non-owning表示意味着它不拥有字符串的内存而是通过指针和长度来引用现有的字符串数据。
std::string_view定义于C标准库头文件string_view中std::string_view的定义如下
namespace std {templateclass charT, class traits std::char_traitscharTclass basic_string_view {public:// 构造函数constexpr basic_string_view() noexcept;constexpr basic_string_view(const charT* str);constexpr basic_string_view(const charT* str, size_t len);// 成员函数constexpr const charT* data() const noexcept;constexpr size_t size() const noexcept;constexpr bool empty() const noexcept;constexpr charT operator[](size_t pos) const;constexpr charT front() const;constexpr charT back() const;constexpr basic_string_view substr(size_t pos, size_t count npos) const;constexpr int compare(basic_string_view other) const noexcept;constexpr size_t find(basic_string_view str, size_t pos 0) const noexcept;// ...};// 类型别名using string_view basic_string_viewchar;using wstring_view basic_string_viewwchar_t;using u16string_view basic_string_viewchar16_t;using u32string_view basic_string_viewchar32_t;
}std::string_view实际上是一种模板类basic_string_view的一种实现。与之类似的还有wstring_view、u8string_view、u16string_view、u32string_view。
std::string_view的特点
轻量级std::string_view本身只包含一个指向字符串数据的指针和一个长度因此它的大小非常小。非拥有式std::string_view不拥有字符串数据的内存它只是对现有字符串数据的引用。这意味着它可以安全地引用临时字符串、字符串字面量或其他字符串对象而无需复制数据。零拷贝由于std::string_view不拥有字符串数据它可以在不进行数据复制的情况下对字符串进行操作。不可变性std::string_view是只读的它提供了一系列成员函数来访问和操作字符串数据但不能修改字符串的内容。字符串操作支持std::string_view提供了一组成员函数例如data()、size()、empty()、substr()、compare()和find()等使得对字符串数据的常见操作变得方便和高效。
通过使用std::string_view可以在不引入额外的内存开销的情况下对字符串进行查看和操作这在许多情况下都是非常有用的。
相比传统的字符串类型如std::string或C风格的字符串传统的字符串类型如std::string或C风格的字符串需要进行内存分配和拷贝操作导致额外的开销和性能损失。而std::string_view则更加轻量级和高效适用于对字符串进行读取和操作特别是在函数参数传递、字符串处理和性能敏感的场景下。
需要注意的是由于std::string_view只是对字符串的引用使用时需要确保字符串的生命周期长于std::string_view的使用范围以避免悬空引用或访问已释放的内存。
std::string_view是C17中引入的一种轻量级字符串视图类型用于以非拥有non-owning的方式引用字符串数据。它提供了一种有效的方式来访问字符串而无需进行复制或拥有内存。
2.1、构造函数
//默认构造函数
constexpr basic_string_view() noexcept;
//拷贝构造函数
constexpr basic_string_view(const string_view other) noexcept default;
//直接构造构造一个从s所指向的字符数组开始的前count个字符的视图
constexpr basic_string_view(const CharT* s, size_type count);
//直接构造构造一个从s所指向的字符数组开始到\0之前为止的视图不包含空字符
constexpr basic_string_view(const CharT* s);std::string_view的构造方法
默认构造方法std::string_view()创建一个空的string_view。字符串指针构造方法std::string_view(const char* str)创建一个string_view指向以null结尾的C风格字符串。字符串指针和长度构造方法std::string_view(const char* str, size_t len)创建一个string_view指向给定长度的字符序列。std::string构造方法std::string_view(const std::string str)创建一个string_view指向std::string对象的字符序列。字符串迭代器构造方法std::string_view(InputIt first, InputIt last)创建一个string_view指向[first, last)区间内的字符序列。
std::string类重载了从string到string_view的转换操作符
operator std::basic_string_viewCharT, Traits() const noexcept;因此可以通过std::string来构造一个std::string_view
std::string_view foo(std::string(LionLong));这个过程其实包含三步
构造std::string的临时对象a通过转换操作符将临时对象a转换为string_view类型的临时对象b调用std::string_view的拷贝构造函数。
2.2、成员函数
std::string_view的成员函数和操作符
data()返回string_view所指向的字符序列的指针。size()、length()返回string_view所指向的字符序列的长度。max_size()返回可以容纳的最大长度。empty()检查string_view是否为空即长度是否为0。operator[]()访问string_view中指定位置的字符。at()以安全的方式访问string_view中指定位置的字符会进行边界检查。front()返回string_view中第一个字符。find()返回首次出现给定子串的位置。back()返回string_view中最后一个字符。begin()返回指向string_view中第一个字符的迭代器。end()返回指向string_view末尾的迭代器。cbegin()返回指向string_view中第一个字符的const迭代器。cend()返回指向string_view末尾的const迭代器。substr()返回一个新的string_view包含原始string_view的子字符串。不同于std::string::substr()的时间复杂度O(n)它的时间复杂度是O(1)。remove_prefix()移除前缀将string_view的起始位置向后移动指定数量的字符。remove_suffix()移除后缀将string_view的结束位置向前移动指定数量的字符。swap()交换两个string_view的内容。compare()比较两个视图是否相等。starts_with() C20新增判断视图是否以以给定的前缀开始。ends_with()C20新增判断视图是否以给定的后缀结尾。contains()C23新增判断视图是否包含给定的子串。
这些成员函数与std::basic_string的相同成员函数完全兼容可以认为是对其调用的一层封装。不同于std::basic_string::data()和字符串字面量data()可以返回指向非空终止的缓冲区的指针。
data()示例
#include string_viewusing namespace std::string_view_literals;int main() {std::string_view sv(hello, LionLong);std::cout sv sv , size() sv.size() , data() sv.data() std::endl;std::string_view sv2 sv.substr(0, 5);std::cout sv2 sv2 , size() sv2.size() , data() sv2.data() std::endl;std::string_view sv3 hello\0 LionLongsv;//std::string_view sv4(hello\0 LionLongsv)std::cout sv3 sv3 , size() sv3.size() , data() sv3.data() std::endl;std::string_view sv4(hello\0 LionLong);std::cout sv4 sv4 , size() sv4.size() , data() sv4.data() std::endl;
}输出
sv hello, LionLong, size() 14, data() hello, LionLong
sv2 hello, size() 5, data() hello, LionLong
sv3 hello LionLong, size() 14, data() hello
sv4 hello, size() 5, data() hello可以看到data()会返回的是起始位置的字符指针const char*以data()返回值进行打印会一直输出直到遇到空字符。因此使用data()需要非常小心。
max_size()示例
std::string_view sv;
std::cout sv.max_size() std::endl; //4611686018427387899remove_prefix()示例视图的起始位置向后移动n位收缩视图的大小。
std::string str hello;
std::string_view v str;
v.remove_prefix(std::min(v.find_first_not_of( ), v.size()));
std::cout String: str , View : v std::endl;
//输出
// String: hello, View : hello三、std::string_view为什么性能高 std::string_view采用享元设计模式通常以ptr和length的结构来实现非常轻便。 std::string_view上的字符串操作具有和std::string同类操作一致的复杂度。 std::string_view中的字符串操作大多数是constexpr的都可在编译器执行省去了运行时的复杂度。
四、std::string_view的使用陷阱
前面介绍data()函数的时候有提到过data()会返回的是起始位置的字符指针若以其返回值进行输出打印会一直输出直到遇到\0结束符。std::string_view不持有所指向内容的所有权所以如果把std::string_view局部变量作为函数返回值则在函数返回后内存会被释放将出现悬垂指针或悬垂引用。由于std::string_view只是字符串数据的视图并不拥有字符串数据它不能用于修改原始字符串的内容。如果尝试修改std::string_view所引用的字符串数据将导致未定义行为。如果需要修改字符串数据应该使用std::string而不是std::string_view。当使用std::string_view时需要注意空指针的风险。如果将一个空指针传递给std::string_view它的行为是未定义的。在使用std::string_view之前应该检查字符串指针是否为空以避免潜在的问题。
std::string_view foo() {std::string s { hello, LionLong };return std::string_view { s };
}int main() {std::cout foo() std::endl; //可能的输出;Vreturn 0;
}
五、std::string_view源码解析
//string_view
templatetypename _CharT, typename _Traits std::char_traits_CharT
class basic_string_view
{
public:// typesusing traits_type _Traits;using value_type _CharT;using pointer value_type*;using const_pointer const value_type*;using reference value_type;using const_reference const value_type;using const_iterator const value_type*;using iterator const_iterator;using const_reverse_iterator std::reverse_iteratorconst_iterator;using reverse_iterator const_reverse_iterator;using size_type size_t;using difference_type ptrdiff_t;static constexpr size_type npos size_type(-1);constexpr basic_string_view() noexcept: _M_len{0}, _M_str{nullptr}{ }constexpr basic_string_view(const basic_string_view) noexcept default;constexpr basic_string_view(const _CharT* __str) noexcept: _M_len{traits_type::length(__str)}, _M_str{__str}{ }constexpr basic_string_view(const _CharT* __str, size_type __len) noexcept: _M_len{__len}, _M_str{__str}{ }//...private:size_t _M_len;const _CharT* _M_str;
};
std::string_view的实现并不复杂在底层其实是一个非常简单的结构。std::string_view通常由两个成员变量组成
指向字符串数据的指针通常是const char*。字符串数据的长度。
构造函数只是对这两个成员变量进行初始化。这两个成员变量使得std::string_view能够表示字符串的范围而不需要复制字符串数据。因此它的创建和销毁成本非常低。
这两个成员变量使得std::string_view能够表示字符串的范围而不需要复制字符串数据。因此它的创建和销毁成本非常低。
看一下std::string_view的几个成员函数实现
//string_view class basic_string_view
constexpr const_pointer data() const noexcept
{return this-_M_str;
}constexpr void remove_prefix(size_type __n) noexcept
{__glibcxx_assert(this-_M_len __n);this-_M_str __n;this-_M_len - __n;
}constexpr void remove_suffix(size_type __n) noexcept
{this-_M_len - __n;
}constexpr basic_string_view substr(size_type __pos 0, size_type __n npos) const noexcept(false)
{__pos std::__sv_check(size(), __pos, basic_string_view::substr);const size_type __rlen std::min(__n, _M_len - __pos);return basic_string_view{_M_str __pos, __rlen};
}
内部实现方面std::string_view的成员函数和操作符通常是非常轻量级的。底层实现原理相对简单主要围绕着对指针和长度的操作展开。
六、总结
std::string_view是C17引入的一个非拥有的字符串视图类型它提供了一种轻量级的方式来访问现有字符串数据。std::string_view通过避免字符串复制和内存分配它可以显著提高程序性能并提供方便的字符串处理能力。但是在使用过程中需要注意正确管理原始字符串的生命周期以确保使用的字符串数据有效和安全。