string和string_view的区别
以下是四个代码示例,阐述string和string_view的区别(千问生成)
✅ 示例1:所有权与生命周期(悬空陷阱)
#include <iostream>
#include <string>
#include <string_view>
std::string_view dangerous() {
std::string local = "Temporary Data";
return std::string_view(local); // ❌ 返回指向已销毁内存的视图!
} // local 析构,视图悬空
std::string safe() {
return std::string("Permanent Data"); // ✅ 返回新分配的字符串副本
}
int main() {
// ⚠️ 危险操作(实际运行可能崩溃/乱码,此处仅演示逻辑)
// std::string_view sv = dangerous();
// std::cout << sv << "\n"; // 未定义行为!(注释掉避免实际崩溃)
// ✅ 安全操作
std::string s = safe();
std::cout << "Safe return: " << s << "\n"; // Safe return: Permanent Data
// 💡 正确使用 string_view 的方式:确保底层数据存活
std::string keeper = "I am alive!";
std::string_view valid_view(keeper); // ✅ keeper 生命周期覆盖 valid_view
std::cout << "Valid view: " << valid_view << "\n"; // Valid view: I am alive!
}
输出:
Safe return: Permanent Data
Valid view: I am alive!
🔑 核心:string_view 是“借来的指针”,必须确保“出借方”(底层数据)在使用期间存活!
✅ 示例2:可变性对比(彻底修正逻辑错误!)
#include <iostream>
#include <string>
#include <string_view>
int main() {
// ✅ std::string:真正修改内存内容
std::string s = "Hello";
s[0] = 'J'; // s 变为 "Jello"
s.append(" World"); // s 变为 "Jello World"
std::cout << "Modified string: " << s << "\n"; // Modified string: Jello World
// ✅ std::string_view:只读 + C++20 视图范围调整(不改原数据!)
std::string_view sv = s; // sv 视图整个 "Jello World"
// sv[0] = 'X'; // ❌ 编译错误!C++17/20 均禁止修改底层字符
// C++20 允许调整视图范围(仅改变 sv 自身的起始/长度,s 不变!)
sv.remove_prefix(1); // sv 现在视图 "ello World"(跳过首字符)
sv.remove_suffix(6); // sv 现在视图 "ello"(再跳过后6字符)
std::cout << "Original string (unchanged): " << s << "\n"; // Jello World
std::cout << "Adjusted view: [" << sv << "]\n"; // Adjusted view: [ello]
// 验证:底层数据未被 string_view 操作影响
std::cout << "s[0] still 'J'? " << (s[0] == 'J' ? "YES" : "NO") << "\n"; // YES
}
输出:
Modified string: Jello World
Original string (unchanged): Jello World
Adjusted view: [ello]
s[0] still 'J'? YES
🔑 核心:
string修改 = 真改内存string_view“修改” = 仅调整自己的窗口范围(C++20),绝不碰原始数据!
✅ 示例3:空终止陷阱(彻底修正初始化方式!)
#include <iostream>
#include <string>
#include <string_view>
#include <cstring>
int main() {
// ✅ 正确初始化:显式指定长度,强制包含中间 \0 和后续字符
const char raw[] = "C:\\file\0name.txt"; // 编译期保留全部16字节(含\0)
std::string path(raw, 16); // 关键!带长度构造,保留二进制数据
std::cout << "path.length(): " << path.length() << "\n"; // 16
std::cout << "path.c_str() output: [" << path.c_str() << "]\n"; // [C:\file](遇\0截断)
std::string_view sv(path.data(), path.length());
std::cout << "sv.length(): " << sv.length() << "\n"; // 16
// ❌ 危险:直接输出 sv.data() 会被 \0 截断
std::cout << "sv.data() output: [" << sv.data() << "]\n"; // [C:\file]
// ✅ 安全验证:用 memcmp 比较完整16字节
if (memcmp(sv.data(), raw, 16) == 0) {
std::cout << "✅ sv 完整保留了二进制数据!\n"; // 会输出!
}
// ✅ 安全输出含 \0 的内容(使用 write + length)
std::cout << "Full content (hex): ";
for (size_t i = 0; i < sv.length(); ++i) {
printf("%02X ", static_cast<unsigned char>(sv[i]));
}
std::cout << "\n";
// 🌰 您原代码风险演示
// LOG_INFO << folder.data(); // 若 folder 含 \0,日志可能只显示 "C:\\file"
}
输出:
path.length(): 16
path.c_str() output: [C:\file]
sv.length(): 16
sv.data() output: [C:\file]
✅ sv 完整保留了二进制数据!
Full content (hex): 43 3A 5C 66 69 6C 65 00 6E 61 6D 65 2E 74 78 74
🔑 核心:
\\→ 编译后 1字节\\0→ 编译后 1字节 空字符- 必须用
(buf, len)构造才能保留\0后的数据! - 输出含
\0数据:禁用<<,改用write()或十六进制打印!
✅ 示例4:性能对比(高频调用场景)
#include <iostream>
#include <string>
#include <string_view>
#include <chrono>
void process_string(std::string s) {} // 每次深拷贝
void process_view(std::string_view sv) {} // 仅拷贝指针+长度(16字节)
int main() {
std::string big(10000, 'A'); // 10KB 字符串
// 测试 string 拷贝开销
auto start = std::chrono::high_resolution_clock::now();
for (int i = 0; i < 10000; ++i) process_string(big);
auto mid = std::chrono::high_resolution_clock::now();
// 测试 string_view 零拷贝
for (int i = 0; i < 10000; ++i) process_view(big);
auto end = std::chrono::high_resolution_clock::now();
auto str_us = std::chrono::duration_cast<std::chrono::microseconds>(mid - start).count();
auto view_us = std::chrono::duration_cast<std::chrono::microseconds>(end - mid).count();
std::cout << "std::string 耗时: " << str_us << " μs (深拷贝 100MB)\n";
std::cout << "string_view 耗时: " << view_us << " μs (仅拷贝 160KB)\n";
std::cout << "性能提升: " << (str_us * 1.0 / view_us) << " 倍\n";
}
典型输出(环境不同数值有差异):
std::string 耗时: 15200 μs (深拷贝 100MB)
string_view 耗时: 85 μs (仅拷贝 160KB)
性能提升: 178.8 倍
🔑 核心:高频只读场景,string_view 避免无谓拷贝,性能碾压!
📌 终极使用口诀(结合修正后示例)
| 场景 | 用谁 | 为什么 | 口诀 |
|---|---|---|---|
| 函数只读参数 | string_view |
零拷贝、泛化接口 | “看数据,用视图” |
| 需修改/存储 | std::string |
拥有内存,安全可控 | “要改动,必拥有” |
含 \0 二进制 |
string_view + 显式长度 |
避免截断 | “有 \0,带长度;输出时,用 write" |
| 返回临时字符串 | std::string |
避免悬空 | “返回值,拷贝走” |
| 与 C API 交互 | std::string |
c_str() 保证 \0 结尾 |
“交 C 库,用 c_str()" |
浙公网安备 33010602011771号