C++ 字符串
c++ string 的allocate和cow
string 可以说是c++ stl 中最常用的容器了,首先弄明白 cpp 中的string 到底是在哪里存储的,下面的代码我们重载了全局的new和delete
#include <string>
#include <iostream>
#include <cstdlib>
#include <vector>
using namespace std;
// 重载全局 new
void* operator new(std::size_t size) {
cout << "[Allocating " << size << " bytes]\n";
return malloc(size);
}
// 重载全局的 delete
// void operator delete(void* ptr) noexcept {
// cout << "[Deallocating]\n";
// return free(ptr);
// }
int main(int argc, char *argv[]) {
for (int i = 1; i <= 17; ++i) {
cout << i << ":" << string(i, '*') << "\n";
}
string(16, '*').append("*").append("**");
cout << "---------------------------\n";
string s1(16, '#');
s1.append("#").append("##");
string s2(s1);
return 0;
}运行结果如下:
1:*
2:**
3:***
4:****
5:*****
6:******
7:*******
8:********
9:*********
10:**********
11:***********
12:************
13:*************
14:**************
15:***************
16:[Allocating 17 bytes]
****************
17:[Allocating 18 bytes]
*****************
[Allocating 17 bytes]
[Allocating 33 bytes]
---------------------------
[Allocating 17 bytes]
[Allocating 33 bytes]
[Allocating 20 bytes] // no copy on write通过上面的代码发现:
- size < 16 的短字符串 (SSO) 在栈上分配,无 heap。
- s2(s1) 分配新内存,无 COW。C++11 标准禁止 COW,因为违反异常安全(never break userspace)。
字符串拼接
在 C++ 中拼接字符串有多种方式,但并不是每种方式都具有良好的“品味”。
1. 传统的 operator+ (性能陷阱)
这是最直观的方式,但也是最容易写出垃圾代码的地方。
std::string s1 = "Hello";
std::string s2 = "World";
std::string s3 = s1 + " " + s2; // 产生多个临时对象,涉及多次内存分配和拷贝Linus 视角:如果你在循环里这么写,你就是在浪费 CPU 周期。
2. operator+= 与 append() (实用主义)
如果你只是想在现有字符串末尾追加内容,永远优先使用 += 或 append()。
std::string s = "Hello";
s += " "; // 推荐:直接在原内存操作
s.append("World"); // 同上,适合追加子串或特定长度内容相比 s = s + "...",+= 避免了创建临时对象和不必要的拷贝。这是处理简单拼接的正确姿势。
3. std::stringstream (过时方案)
老牌工具,适合复杂的类型转换。但在现代 C++ 中,它的性能开销(虚函数、缓冲区管理)和冗长的代码让它显得非常过时。
4. 推荐写法:std::format (C++20)
这是目前最优雅、性能也极其出色的方式。它类似于 Python 的 f-string,提供了类型安全且易读的语法。
#include <format>
std::string name = "Linus";
int version = 6;
// 优雅、高效、类型安全
std::string s = std::format("Hello {}, Linux kernel version: {}", name, version);5. 推荐写法:std::print / std::println (C++23)
如果你拼接字符串只是为了输出,不要先拼接成 std::string 再打印。直接使用 C++23 的 std::print 系列函数。
#include <print>
std::string name = "Linus";
// 直接输出,无需显式拼接字符串,性能最优
std::println("Hello {}, welcome to the kernel world.", name);总结:如何选择?
- 简单追加:用
+=。 - 复杂格式化:用
std::format。 - 直接输出:用
std::print。 - 禁止:在循环中使用
+拼接长字符串。
string_view
string_view 是 C++17 引入的一个重要类,它提供了一个轻量级的、非拥有的字符串视图。它本质上是一个指向字符序列的指针加上长度信息。
基本概念
string_view 不拥有字符串数据,它只是引用已存在的字符串数据。这使得它非常轻量,可以高效地传递和操作字符串,而无需复制数据。
主要优点
- 性能: 无需复制字符串数据,避免了内存分配
- 安全性: 有边界检查,避免缓冲区溢出
- 灵活性: 可以安全地引用各种字符串类型(C 风格字符串、std::string 等)
基本用法示例
#include <iostream>
#include <string>
#include <string_view>
int main() {
// 从 C 风格字符串创建
const char* cstr = "Hello World";
std::string_view sv1(cstr);
// 从 std::string 创建
std::string str = "Hello World";
std::string_view sv2(str);
// 直接字面量
std::string_view sv3("Hello World");
// 指定长度
std::string_view sv4(cstr, 5); // "Hello"
// 使用字面量后缀 (C++17)
using namespace std::string_view_literals;
auto sv5 = "Hello World"sv;
std::cout << sv1 << std::endl; // 输出: Hello World
std::cout << sv4 << std::endl; // 输出: Hello
std::cout << sv5 << std::endl; // 输出: Hello World
return 0;
}字面量后缀
C++14 和 C++17 引入了字符串字面量后缀,可以更方便地创建 std::string 和 std::string_view 对象。
std::string 字面量后缀 s (C++14)
#include <string>
#include <iostream>
int main() {
using namespace std::string_literals;
// 使用 s 后缀创建 std::string
auto str1 = "Hello World"s; // 类型为 std::string
auto str2 = "Hello"s + " " + "World"s; // 拼接
std::cout << "Type: " << typeid(str1).name() << std::endl;
std::cout << str1 << std::endl;
std::cout << str2 << std::endl;
return 0;
}优势:
- 避免隐式转换带来的临时对象
- 类型明确,编译器可以更好地优化
- 在模板和重载解析中避免歧义
std::string_view 字面量后缀 sv (C++17)
#include <string_view>
#include <iostream>
int main() {
using namespace std::string_view_literals;
// 使用 sv 后缀创建 std::string_view
auto sv1 = "Hello World"sv; // 类型为 std::string_view
auto sv2 = "Hello"sv.substr(0, 3); // "Hel"
std::cout << "Type: " << typeid(sv1).name() << std::endl;
std::cout << sv1 << std::endl;
std::cout << sv2 << std::endl;
return 0;
}优势:
- 零成本创建 string_view
- 避免不必要的 std::string 构造
- 在函数参数传递时性能最优
字面量后缀的性能对比
#include <string>
#include <string_view>
#include <iostream>
// 接收 std::string_view
void process_sv(std::string_view sv) {
std::cout << "string_view: " << sv << std::endl;
}
// 接收 const std::string&
void process_str(const std::string& str) {
std::cout << "string: " << str << std::endl;
}
int main() {
using namespace std::string_literals;
using namespace std::string_view_literals;
// 字面量直接传递给 string_view:零成本
process_sv("Hello World"); // 推荐
process_sv("Hello World"sv); // 等价,更明确
// 字面量传递给 string:需要构造临时 std::string
process_str("Hello World"); // 产生临时对象
process_str("Hello World"s); // 显式构造,更清晰
return 0;
}实际应用:避免类型歧义
#include <string>
#include <string_view>
#include <iostream>
// 重载函数
void print(std::string_view sv) {
std::cout << "string_view version: " << sv << std::endl;
}
void print(const std::string& str) {
std::cout << "string version: " << str << std::endl;
}
int main() {
using namespace std::string_literals;
using namespace std::string_view_literals;
print("Hello"); // 调用哪个?可能产生歧义
print("Hello"sv); // 明确调用 string_view 版本
print("Hello"s); // 明确调用 string 版本
return 0;
}字面量后缀与 UTF-8
#include <string>
#include <string_view>
#include <iostream>
int main() {
using namespace std::string_literals;
using namespace std::string_view_literals;
// UTF-8 字符串字面量
auto utf8_str = u8"你好世界"s; // std::string (UTF-8)
auto utf8_sv = u8"你好世界"sv; // std::string_view (UTF-8)
std::cout << utf8_str << std::endl;
std::cout << utf8_sv << std::endl;
return 0;
}常用操作
#include <iostream>
#include <string_view>
int main() {
std::string_view sv = "Hello, World!";
// 获取长度
std::cout << "Length: " << sv.length() << std::endl;
// 访问字符
std::cout << "First char: " << sv[0] << std::endl;
std::cout << "Last char: " << sv.at(sv.length()-1) << std::endl;
// 子串操作
std::string_view sub = sv.substr(0, 5); // "Hello"
std::cout << "Substring: " << sub << std::endl;
// 查找
size_t pos = sv.find("World"); // 7
if (pos != std::string_view::npos) {
std::cout << "Found 'World' at position: " << pos << std::endl;
}
// 比较
std::string_view sv2 = "Hello";
if (sv.substr(0, 5) == sv2) {
std::cout << "First 5 chars match 'Hello'" << std::endl;
}
// 修改视图边界(不修改底层数据)
sv.remove_prefix(7); // 移除 "Hello, "
std::cout << "After remove_prefix: " << sv << std::endl; // "World!"
sv.remove_suffix(1); // 移除 "!"
std::cout << "After remove_suffix: " << sv << std::endl; // "World"
return 0;
}与 std::string 的区别
| 特性 | std::string | std::string_view |
|---|---|---|
| 数据拥有权 | 有 | 无 |
| 内存分配 | 可能需要 | 不需要 |
| 大小 | sizeof(std::string) | sizeof(指针+长度) |
| 修改能力 | 可修改 | 只读 |
| 性能 | 较低 | 较高 |
string 和 string_view 的相互转换
string → string_view
从 std::string 转换为 std::string_view 是零成本的,只是简单的指针和长度赋值:
#include <string>
#include <string_view>
int main() {
std::string str = "Hello World";
// 隐式转换(推荐)
std::string_view sv1 = str;
// 显式构造
std::string_view sv2(str);
// 使用构造函数指定范围
std::string_view sv3(str.data(), 5); // "Hello"
return 0;
}注意:转换后的 string_view 必须在原 string 生命周期内使用,否则会产生悬空引用。
string_view → string
从 std::string_view 转换为 std::string 需要内存分配,因为需要复制数据:
#include <string>
#include <string_view>
int main() {
std::string_view sv = "Hello World";
// 隐式转换(C++23 之前不支持,C++23 支持)
// std::string str1 = sv; // C++23 之前编译错误
// 显式构造(推荐)
std::string str1(sv);
// 使用 string_view 的 data() 和 size()
std::string str2(sv.data(), sv.size());
// 使用 assign
std::string str3;
str3.assign(sv.data(), sv.size());
return 0;
}性能提示:只有在真正需要拥有数据或修改数据时才转换为 std::string。
实际应用示例
#include <string>
#include <string_view>
#include <iostream>
// 接收 string_view,避免不必要的拷贝
void process_data(std::string_view data) {
std::cout << "Processing: " << data << std::endl;
// 如果需要修改数据,转换为 string
if (data.find("modify") != std::string_view::npos) {
std::string str(data); // 转换为 string
str += " [modified]";
std::cout << "Modified: " << str << std::endl;
}
}
int main() {
std::string str = "Hello World";
process_data(str); // string → string_view (零成本)
process_data("Literal string"); // 字面量 → string_view (零成本)
return 0;
}转换性能对比
#include <string>
#include <string_view>
#include <chrono>
int main() {
std::string str(1000000, 'x');
// string → string_view:极快(纳秒级)
auto start = std::chrono::high_resolution_clock::now();
std::string_view sv = str;
auto end = std::chrono::high_resolution_clock::now();
// 几乎没有开销
// string_view → string:需要分配和复制内存
start = std::chrono::high_resolution_clock::now();
std::string str2(sv);
end = std::chrono::high_resolution_clock::now();
// 需要分配 1MB 内存并复制数据
return 0;
}作为函数参数:string_view vs const std::string&
这是 string_view 最核心的使用场景。
- const std::string&:
- 如果传入
std::string,性能尚可(仅引用)。 - 如果传入
"literal"或char*,会触发隐式转换,产生一个临时的std::string对象,导致不必要的堆内存分配和释放。
- 如果传入
- std::string_view:
- 无论传入
std::string、"literal"还是char*,都只是简单的指针赋值和长度计算,零内存分配。 - 建议按值传递:
string_view足够小(通常是两个指针的大小),按值传递比按引用传递更利于编译器优化(寄存器传参)。
- 无论传入
使用注意事项
- 生命周期 (Dangling View): 必须确保被引用的字符串数据在
string_view使用期间保持有效。std::string_view get_view() { std::string s = "temporary"; return std::string_view(s); // 危险!返回了局部变量的视图 } - 只读:
string_view不允许修改底层数据。如果需要修改,请使用std::string。 - 非空终止 (Null-termination):
string_view不保证以\0结尾。- 绝不要把
sv.data()直接传给需要 C 风格字符串的函数(如printf,strlen,open)。 - 如果必须调用这类 API,请先将其转换为
std::string:std::string(sv).c_str()。
- 绝不要把
实际应用场景
// 函数参数:避免不必要的字符串复制
void process_text(std::string_view text) {
std::cout << "Processing: " << text << " (length: " << text.length() << ")" << std::endl;
}
int main() {
// 可以传入不同类型的字符串而无需转换
process_text("C-style string"); // 字面量
process_text(std::string("std::string")); // std::string
const char* ptr = "Pointer to string";
process_text(std::string_view(ptr, 6)); // 自定义视图
return 0;
}使用 string_view 可以显著提高程序性能,特别是在需要频繁传递字符串参数但不需要修改字符串的场景中。
string_view 在容器中存储
string_view 可以存储在标准容器中,但需要特别注意生命周期管理问题。
在序列容器中存储
vector<string_view> - 高性能但需谨慎
#include <iostream>
#include <string>
#include <string_view>
#include <vector>
int main() {
std::vector<std::string> strings = {
"Hello",
"World",
"C++"
};
// 创建 string_view 容器,引用 strings 中的数据
std::vector<std::string_view> views;
views.reserve(strings.size());
for (const auto& str : strings) {
views.emplace_back(str); // 零成本引用
}
// 安全使用:strings 仍然存活
for (const auto& view : views) {
std::cout << view << std::endl;
}
return 0;
}危险示例 - 悬空引用:
#include <iostream>
#include <string>
#include <string_view>
#include <vector>
std::vector<std::string_view> create_views() {
std::vector<std::string_view> views;
// 危险!临时对象在函数结束时销毁
views.emplace_back("temporary"); // 字面量安全(静态存储)
views.emplace_back(std::string("temporary2")); // 危险!临时对象销毁
return views; // 返回的 views 包含悬空引用
}
int main() {
auto views = create_views();
// 未定义行为!访问已销毁的字符串
std::cout << views[1] << std::endl;
return 0;
}正确的生命周期管理
#include <iostream>
#include <string>
#include <string_view>
#include <vector>
class StringPool {
private:
std::vector<std::string> pool_;
std::vector<std::string_view> views_;
public:
// 添加字符串到池中,返回视图
std::string_view add(std::string_view sv) {
pool_.emplace_back(sv);
views_.emplace_back(pool_.back());
return views_.back();
}
// 获取所有视图
const std::vector<std::string_view>& getViews() const {
return views_;
}
};
int main() {
StringPool pool;
// 安全:pool 拥有数据,views 引用 pool 中的数据
pool.add("Hello");
pool.add("World");
pool.add(std::string("Dynamic"));
for (const auto& view : pool.getViews()) {
std::cout << view << std::endl;
}
return 0;
}性能对比:vectorvs vector<string_view>
#include <iostream>
#include <string>
#include <string_view>
#include <vector>
#include <chrono>
void benchmark_vector_string() {
auto start = std::chrono::high_resolution_clock::now();
std::vector<std::string> strings;
strings.reserve(100000);
for (int i = 0; i < 100000; ++i) {
strings.emplace_back("String number " + std::to_string(i));
}
auto end = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
std::cout << "vector<string>: " << duration.count() << " ms" << std::endl;
}
void benchmark_vector_string_view() {
auto start = std::chrono::high_resolution_clock::now();
std::vector<std::string> pool;
std::vector<std::string_view> views;
pool.reserve(100000);
views.reserve(100000);
for (int i = 0; i < 100000; ++i) {
std::string str = "String number " + std::to_string(i);
pool.emplace_back(str);
views.emplace_back(pool.back());
}
auto end = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
std::cout << "vector<string_view>: " << duration.count() << " ms" << std::endl;
}
int main() {
benchmark_vector_string();
benchmark_vector_string_view();
return 0;
}结果分析:
vector<string>:每次插入都可能触发内存分配和字符串拷贝vector<string_view>:插入 string_view 是零成本的,但需要额外的字符串池管理
string_view 作为关联容器的 key
string_view 可以作为 std::map、std::unordered_map、std::set、std::unordered_set 等关联容器的 key,这可以显著提高性能,但同样需要注意生命周期问题。
map<string_view, T> - 有序映射
#include <iostream>
#include <string>
#include <string_view>
#include <map>
int main() {
// 使用 string_view 作为 key
std::map<std::string_view, int> config;
// 字面量作为 key(安全,静态存储)
config["timeout"] = 30;
config["max_retries"] = 3;
config["buffer_size"] = 1024;
// 查询
std::string_view key = "timeout";
if (config.contains(key)) {
std::cout << key << " = " << config[key] << std::endl;
}
// 遍历
for (const auto& [k, v] : config) {
std::cout << k << ": " << v << std::endl;
}
return 0;
}unordered_map<string_view, T> - 哈希映射
#include <iostream>
#include <string>
#include <string_view>
#include <unordered_map>
int main() {
// 使用 string_view 作为 key
std::unordered_map<std::string_view, int> cache;
// 字面量作为 key
cache["user_1"] = 100;
cache["user_2"] = 200;
cache["user_3"] = 300;
// 查询
std::string_view user = "user_2";
auto it = cache.find(user);
if (it != cache.end()) {
std::cout << "Found: " << it->first << " -> " << it->second << std::endl;
}
return 0;
}set<string_view> 和 unordered_set<string_view>
#include <iostream>
#include <string>
#include <string_view>
#include <set>
#include <unordered_set>
int main() {
// 有序集合
std::set<std::string_view> keywords = {
"if", "else", "for", "while", "return"
};
std::string_view word = "for";
if (keywords.contains(word)) {
std::cout << word << " is a keyword" << std::endl;
}
// 无序集合
std::unordered_set<std::string_view> stop_words = {
"the", "a", "an", "and", "or"
};
std::string_view text = "the";
if (stop_words.contains(text)) {
std::cout << text << " is a stop word" << std::endl;
}
return 0;
}生命周期陷阱
#include <iostream>
#include <string>
#include <string_view>
#include <unordered_map>
std::unordered_map<std::string_view, int> create_bad_map() {
std::unordered_map<std::string_view, int> map;
// 危险!临时对象销毁后,map 中的 key 变成悬空引用
std::string temp = "temporary";
map[temp] = 42;
return map; // 返回的 map 包含悬空引用
}
int main() {
auto map = create_bad_map();
// 未定义行为!访问悬空引用
for (const auto& [k, v] : map) {
std::cout << k << ": " << v << std::endl; // 崩溃或输出垃圾数据
}
return 0;
}正确的生命周期管理
方案 1:使用字符串池
#include <iostream>
#include <string>
#include <string_view>
#include <unordered_map>
class StringKeyedMap {
private:
std::vector<std::string> key_pool_;
std::unordered_map<std::string_view, int> map_;
public:
// 插入键值对
void insert(std::string_view key, int value) {
key_pool_.emplace_back(key);
map_[key_pool_.back()] = value;
}
// 查找
bool contains(std::string_view key) const {
return map_.contains(key);
}
// 获取值
int get(std::string_view key) const {
auto it = map_.find(key);
return it != map_.end() ? it->second : 0;
}
// 遍历
void forEach(auto&& func) const {
for (const auto& [k, v] : map_) {
func(k, v);
}
}
};
int main() {
StringKeyedMap config;
config.insert("timeout", 30);
config.insert("max_retries", 3);
config.insert("buffer_size", 1024);
config.forEach([](std::string_view key, int value) {
std::cout << key << ": " << value << std::endl;
});
return 0;
}方案 2:使用静态字符串
#include <iostream>
#include <string>
#include <string_view>
#include <unordered_map>
// 配置类,使用静态字符串作为 key
class Config {
private:
std::unordered_map<std::string_view, int> config_;
public:
Config() {
// 使用字面量(静态存储,安全)
config_["timeout"] = 30;
config_["max_retries"] = 3;
config_["buffer_size"] = 1024;
}
int get(std::string_view key, int default_value = 0) const {
auto it = config_.find(key);
return it != config_.end() ? it->second : default_value;
}
void set(std::string_view key, int value) {
config_[key] = value;
}
};
int main() {
Config config;
std::cout << "timeout: " << config.get("timeout") << std::endl;
std::cout << "unknown: " << config.get("unknown", -1) << std::endl;
return 0;
}性能对比:map<string, T> vs map<string_view, T>
#include <iostream>
#include <string>
#include <string_view>
#include <map>
#include <unordered_map>
#include <chrono>
void benchmark_map_string() {
auto start = std::chrono::high_resolution_clock::now();
std::map<std::string, int> map;
for (int i = 0; i < 100000; ++i) {
map["key_" + std::to_string(i)] = i;
}
auto end = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
std::cout << "map<string>: " << duration.count() << " ms" << std::endl;
}
void benchmark_map_string_view() {
auto start = std::chrono::high_resolution_clock::now();
std::vector<std::string> pool;
std::map<std::string_view, int> map;
for (int i = 0; i < 100000; ++i) {
std::string key = "key_" + std::to_string(i);
pool.emplace_back(key);
map[pool.back()] = i;
}
auto end = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
std::cout << "map<string_view>: " << duration.count() << " ms" << std::endl;
}
void benchmark_unordered_map_string() {
auto start = std::chrono::high_resolution_clock::now();
std::unordered_map<std::string, int> map;
for (int i = 0; i < 100000; ++i) {
map["key_" + std::to_string(i)] = i;
}
auto end = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
std::cout << "unordered_map<string>: " << duration.count() << " ms" << std::endl;
}
void benchmark_unordered_map_string_view() {
auto start = std::chrono::high_resolution_clock::now();
std::vector<std::string> pool;
std::unordered_map<std::string_view, int> map;
for (int i = 0; i < 100000; ++i) {
std::string key = "key_" + std::to_string(i);
pool.emplace_back(key);
map[pool.back()] = i;
}
auto end = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
std::cout << "unordered_map<string_view>: " << duration.count() << " ms" << std::endl;
}
int main() {
benchmark_map_string();
benchmark_map_string_view();
benchmark_unordered_map_string();
benchmark_unordered_map_string_view();
return 0;
}性能分析:
map<string_view>vsmap<string>:避免了 key 的字符串拷贝,但需要额外的字符串池管理unordered_map<string_view>vsunordered_map<string>:哈希计算更快(无需分配内存),但同样需要字符串池
实际应用:配置解析器
#include <iostream>
#include <string>
#include <string_view>
#include <unordered_map>
#include <sstream>
class ConfigParser {
private:
std::vector<std::string> key_pool_;
std::vector<std::string> value_pool_;
std::unordered_map<std::string_view, std::string_view> config_;
public:
// 解析配置行 "key=value"
void parseLine(std::string_view line) {
auto pos = line.find('=');
if (pos == std::string_view::npos) {
return; // 无效格式
}
auto key = line.substr(0, pos);
auto value = line.substr(pos + 1);
// 存储到池中
key_pool_.emplace_back(key);
value_pool_.emplace_back(value);
// 使用 string_view 作为 key
config_[key_pool_.back()] = value_pool_.back();
}
// 获取配置值
std::string_view get(std::string_view key) const {
auto it = config_.find(key);
return it != config_.end() ? it->second : "";
}
// 获取整数配置
int getInt(std::string_view key, int default_value = 0) const {
auto sv = get(key);
if (sv.empty()) {
return default_value;
}
int value = 0;
std::from_chars(sv.data(), sv.data() + sv.size(), value);
return value;
}
// 遍历所有配置
void forEach(auto&& func) const {
for (const auto& [k, v] : config_) {
func(k, v);
}
}
};
int main() {
ConfigParser parser;
// 解析配置
parser.parseLine("timeout=30");
parser.parseLine("max_retries=3");
parser.parseLine("buffer_size=1024");
parser.parseLine("debug_mode=true");
// 获取配置
std::cout << "timeout: " << parser.getInt("timeout") << std::endl;
std::cout << "max_retries: " << parser.getInt("max_retries") << std::endl;
std::cout << "buffer_size: " << parser.getInt("buffer_size") << std::endl;
std::cout << "debug_mode: " << parser.get("debug_mode") << std::endl;
// 遍历所有配置
std::cout << "\nAll configs:" << std::endl;
parser.forEach([](std::string_view key, std::string_view value) {
std::cout << " " << key << " = " << value << std::endl;
});
return 0;
}总结:何时使用 string_view
✅ 适合使用 string_view 的场景
函数参数:避免不必要的字符串拷贝
void process(std::string_view text); // 推荐字符串字面量作为容器 key:静态存储,生命周期安全
std::unordered_map<std::string_view, int> map = {{"key1", 1}, {"key2", 2}};字符串池管理:集中管理字符串生命周期
class StringPool { /* ... */ };子串操作:避免创建临时字符串
std::string_view sub = str.substr(0, 5);
❌ 不适合使用 string_view 的场景
需要拥有数据:需要修改或长期持有字符串
std::string str = sv; // 需要拷贝生命周期不确定:无法保证被引用的字符串存活
std::string_view get_view() { /* 危险 */ }需要空终止符:调用 C 风格 API
printf("%s", sv.data()); // 危险 printf("%s", std::string(sv).c_str()); // 安全作为返回值:除非返回的是静态字符串或字符串池中的视图
性能建议
- 优先使用 string_view 作为函数参数:零成本传递
- 谨慎在容器中存储 string_view:必须管理好生命周期
- 使用字符串池模式:集中管理字符串,避免悬空引用
- 性能测试:在实际场景中测试,不要过早优化
Summary:string_view 是个好工具,但别滥用。如果你搞不清楚生命周期,就老老实实用 std::string。悬空指针比内存分配更糟糕。





