C++笔记:std::string_view

std::string_view 是 C++17 引入的 轻量级字符串"只读视图"

它不拥有字符串,只是"指向一段连续字符"的 (ptr, length) 二元组。

核心目的:

避免创建临时 std::string、减少拷贝,提高性能。

cpp 复制代码
void f(std::string_view s);

你可以传:

  • std::string

  • const char*

  • 字面量 "hello"

  • char[]

并且不用分配内存、不拷贝内容

内部原理

如果你去翻源码(我这里放MSVC的实现源码),你会发现它的关键点就在这两个成员变量里

cpp 复制代码
using string_view = basic_string_view<char>;

template <class _Elem, class _Traits>
class basic_string_view {
public:
    using const_pointer          = const _Elem*;
    using size_type              = size_t;
private:
    const_pointer _Mydata;
    size_type _Mysize;
};

说人话就是

cpp 复制代码
class string_view {
    const char* data_;   // 指向外部的字符串内容
    size_t size_;        // 长度
};
  • 没有 '\0' 终止符的要求

  • 没有内存所有权

  • 不会自动管理被观察数据的生命周期

它仅仅是一个"窗口",与实际的存储分离。(不知道读者是否用过pytorch,这就很像Tensor里storage和view的关系)

所以:

cpp 复制代码
std::string_view sv = some_string.substr(...);  // OK,zero-copy

但是:

cpp 复制代码
std::string_view sv;
{
    std::string s = "abc";
    sv = s;     // sv 指向 s 的内部 storage
}              // s 析构 → sv 悬空

==> 悬空引用

string_view 的主要 API

构造函数

cpp 复制代码
constexpr string_view() noexcept;//空视图,data=nullptr, size=0。
constexpr string_view(const char* s);//空视图,data=nullptr, size=0。
constexpr string_view(const char* s, size_t count);//从指针 + 长度构造,不需要 '\0'。
constexpr string_view(const std::string& str) noexcept;//引用 str.data(),str.size()。

数据访问

cpp 复制代码
constexpr const char* data() const noexcept;
constexpr size_t size() const noexcept;
constexpr bool empty() const noexcept;
constexpr const char& operator[](size_t pos) const;
constexpr const char& at(size_t pos) const;
constexpr const char& front() const;
constexpr const char& back() const;
//还有begin,end等等迭代器,和string一样

字符串操作

cpp 复制代码
//不分配,不复制,仍然只是 view。
constexpr string_view substr(size_t pos = 0, size_t count = npos) const;
//负 → 小,正 → 大。
constexpr int compare(string_view sv) const noexcept;

//find 系列
size_t find(string_view sv, size_t pos = 0) const noexcept;
size_t find(char c, size_t pos = 0) const noexcept;
size_t rfind(...);
size_t find_first_of(...);
size_t find_last_of(...);
size_t find_first_not_of(...);
size_t find_last_not_of(...);

修改 view 本身

不改变底层字符,只是移动窗口。

cpp 复制代码
constexpr void remove_prefix(size_t n);
constexpr void remove_suffix(size_t n);

例子:

cpp 复制代码
std::string_view sv = "hello";
sv.remove_prefix(1);  // "ello"
sv.remove_suffix(2);  // "el"

转换回 string

cpp 复制代码
std::string s = std::string(view); // 复制!

这会真的 copy 内容。

string_view 使用注意事项

悬空引用

cpp 复制代码
std::string_view sv;
{
    std::string tmp = "abc";
    sv = tmp;  // 指向 tmp.data()
}              // tmp销毁 → sv悬空

指向临时字符串也有问题

cpp 复制代码
std::string_view sv = std::string("abc"); // 临时对象立即销毁

指向 vector<char> resize 后失效

底层存储可能重新分配。

不保证 null-terminated

你不能把 string_view.data() 当 C 字符串用。

cpp 复制代码
printf("%s", sv.data()); // 如果现在不是 '\0' 结尾 → UB

与const string &的区别

其实看到api也大概能感受到了,string_view作为窗口,仅仅起到观察的效果,不拥有资源。并且这个窗口本身也是可以进行修改的。

所以如果仅仅是一次性的引用string,那么这两者几乎没区别

但是如果使用时需要调整,比如需要取substr,如果使用const string&,就会额外拷贝一个string出来。但是如果使用string_view,那么仅仅是新开一个窗口观察,开销更小。

当然,由于string_view是只读的,如果需要修改字符串,还是需要string&的

相关推荐
玫瑰花店2 小时前
万字C++中锁机制和内存序详解
开发语言·c++·算法
D_evil__2 小时前
[C++高频精进] 文件IO:文件流
c++
西幻凌云2 小时前
认识STL序列式容器——List
开发语言·c++·stl·list·序列式容器
~无忧花开~3 小时前
JavaScript实现PDF本地预览技巧
开发语言·前端·javascript
靠沿3 小时前
Java数据结构初阶——LinkedList
java·开发语言·数据结构
4***99743 小时前
Kotlin序列处理
android·开发语言·kotlin
froginwe113 小时前
Scala 提取器(Extractor)
开发语言
t***D2643 小时前
Kotlin在服务端开发中的生态建设
android·开发语言·kotlin