std::string_view
std::string_view 是 C++17 引入的一个非常实用的轻量级字符串工具。简单来说,它就像是给一段现有的字符串拍了一张“快照”或提供了一个“观察窗口”。
它本身不拥有字符串的数据,也不负责管理内存,只是简单地记录了一个指针(指向字符串的开头)和一个长度。
在 C++17 之前,我们写函数接收字符串参数时,通常有两种选择,但它们都有各自的痛点:
const std::string&:看似高效,但如果你传入一个字符串字面量(比如"hello"),编译器会偷偷在后台创建一个临时的std::string对象,这会产生不必要的内存分配和拷贝开销。const char*:虽然轻量,但它不知道字符串有多长(需要遍历找\0),而且不安全,接口也不够现代。
std::string_view 完美解决了这些问题,它的核心优势在于:
- 零拷贝(Zero-copy):无论是传入
std::string、字符串字面量("hello")还是字符数组,它都不会拷贝数据,只是记录一下指针和长度,构造成本极低。 - 接口统一:一个
std::string_view参数就能通吃各种类型的字符串来源,不再需要写多个函数重载。 - 高效的切片操作:当你需要截取子串时,
std::string的substr()会分配新内存并拷贝数据;而std::string_view的substr()仅仅是移动了一下指针、改了一下长度,时间复杂度是 O(1),完全不消耗额外内存
#include <iostream>
#include <string>
#include <string_view>
// 最佳实践:只读字符串参数,优先使用 string_view
void print_info(std::string_view sv) {
std::cout << "内容: " << sv << ", 长度: " << sv.length() << std::endl;
}
int main() {
std::string str = "Hello C++";
const char* c_str = "Hello C";
// 无论是 std::string 还是 const char*,都能完美接收,且无额外拷贝
print_info(str);
print_info(c_str);
print_info("字面量");
// 高效的切片(截取子串),不产生新内存分配
std::string_view sv = str;
std::string_view sub = sv.substr(0, 5); // 只指向 "Hello",不拷贝
std::cout << "子串: " << sub << std::endl;
return 0;
}
std::string_view 虽然快,但它把内存管理的责任完全交给了你。因为它不拥有数据,所以使用时必须严格遵守以下规则:
严防“悬空视图”(Dangling View):
这是最大的陷阱!你必须保证原始字符串的生命周期长于 string_view。绝对不能返回指向局部变量的 string_view。
// ❌ 错误示范:极其危险!
std::string_view get_bad_view() {
std::string local_str = "临时数据";
return local_str; // 函数结束 local_str 被销毁,返回的视图指向了已释放的内存!
}
不保证以 \0 结尾:
C 语言风格的函数(如 printf("%s")、fopen 等)依赖字符串末尾的 \0 来判断结束。std::string_view 可能会指向字符串中间的一段,末尾没有 \0。因此,不能直接把 sv.data() 传给老旧的 C 语言 API。
只读不可修改:
它只是一个“视图”,你不能通过它来修改字符串的内容,也不能用它来拼接、插入或删除字符。
用 std::string:当你需要拥有这段字符串、需要修改它、需要长期保存(比如存入类成员变量或容器中),或者不想操心原始数据什么时候被释放时。
用 std::string_view:当你的函数只是“看一眼”字符串(只读参数),或者在做高频的字符串解析、切片、分词,且能保证原始数据一直有效时。