std::string_view能避免拷贝是因为它不拥有数据,只保存指向已有内存的指针和长度,不分配堆内存、不调用构造/析构函数,所有操作均为只读视图。
因为 std::string_view 不拥有字符串数据,只保存指向已有内存的 const char* 和长度。它不分配堆内存,也不调用构造/析构函数,所有操作都是只读视图——传参、切片、比较都不触发复制。
常见错误是把临时 std::string 的 c_str() 交给 string_view:比如 std::string_view{std::to_string(42).c_str()},此时 std::to_string 返回的临时对象在表达式结束就销毁,string_view 持有悬垂指针。
string_view(如全局字符串字面量、长期存活的 std::string 对象)const std::string&,尤其当函数内部只读且不需修改或存储时string_view 无法隐式转成 std::string;需要显式构造,这会触发拷贝——别在不该转的地方转std::string 提供了 sv 字面量后缀(C++17)和 std::string_view 构造函数,但关键在于生命周期管理。
错误示例:返回局部 string 的 string_view:
立即学习“C++免费学习笔记(深入)”;
std::string_view bad() {
std::string s = "hello";
return std::string_view{s}; // ❌ s 在函数返回时析构,view 悬垂
}正确方式:
std::string_view{"hello"}(自动推导长度,无 null 终止符依赖)std::string 变量:std::string s = "hello"; std::string_view sv = s;
sv 后缀(需包含 ):using namespace std::string_literals; auto sv = "hello"sv;
string_view 是 constexpr,可用于模板非类型参数(如 static_assert(sv.size() == 5))string_view 不依赖 null 终止符,长度明确;const char* 必须以 \0 结尾,否则 strlen 等函数会越界读取。
这意味着:
string_view 可安全持有二进制数据或含 \0 的片段(如解析协议包、文件头)string_view::data() 返回的指针不一定以 \0 结尾,不能直接传给 C 函数(如 printf("%s", sv.data()) 会崩溃)std::string{sv}.c_str()(代价是拷贝),不需要则用 sv.data() + sv.size() 成对传递sv1 == sv2 是 O(min(len)),而 strcmp(a, b) 是 O(n),且后者可能读越界很多标准库函数在 C++17 后重载支持 string_view,但开发者常忽略它们的存在。
std::stoi、std::stod 等转换函数新增了 string_view 版本,避免为数字子串额外构造 std::string
std::filesystem::path 构造函数接受 string_view,解析路径片段时可直接切片原始字符串std::regex_match 的 std::smatch 子匹配结果是 std::sub_match,其基类就是 string_view 的封装,可直接用于后续处理而无需拷贝std::string_view 没有 find_first_of 等高级查找方法(只有 find、rfind),需要手写或借助算法(如 std::find_first_of(sv.begin(), sv.end(), ...))最易被忽略的一点:string_view 的 remove_prefix 和 remove_suffix 是 O(1) 常数时间,而对应 std::string 的 s 是 O(n) —— 解析分隔符(如 CSV、HTTP header)时,反复切片用
ubstrstring_view 能省下大量内存分配和拷贝。