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

通过上面的代码发现:

  1. size < 16 的短字符串 (SSO) 在栈上分配,无 heap。
  2. 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 不拥有字符串数据,它只是引用已存在的字符串数据。这使得它非常轻量,可以高效地传递和操作字符串,而无需复制数据。

主要优点

  1. 性能: 无需复制字符串数据,避免了内存分配
  2. 安全性: 有边界检查,避免缓冲区溢出
  3. 灵活性: 可以安全地引用各种字符串类型(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::stringstd::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::stringstd::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 足够小(通常是两个指针的大小),按值传递比按引用传递更利于编译器优化(寄存器传参)。

使用注意事项

  1. 生命周期 (Dangling View): 必须确保被引用的字符串数据在 string_view 使用期间保持有效。
    std::string_view get_view() {
        std::string s = "temporary";
        return std::string_view(s); // 危险!返回了局部变量的视图
    }
  2. 只读: string_view 不允许修改底层数据。如果需要修改,请使用 std::string
  3. 非空终止 (Null-termination): string_view 不保证以 \0 结尾。
    • 绝不要把 sv.data() 直接传给需要 C 风格字符串的函数(如 printf, strlen, open)。
    • 如果必须调用这类 API,请先将其转换为 std::stringstd::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::mapstd::unordered_mapstd::setstd::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> vs map<string>:避免了 key 的字符串拷贝,但需要额外的字符串池管理
  • unordered_map<string_view> vs unordered_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 的场景

  1. 函数参数:避免不必要的字符串拷贝

    void process(std::string_view text); // 推荐
  2. 字符串字面量作为容器 key:静态存储,生命周期安全

    std::unordered_map<std::string_view, int> map = {{"key1", 1}, {"key2", 2}};
  3. 字符串池管理:集中管理字符串生命周期

    class StringPool { /* ... */ };
  4. 子串操作:避免创建临时字符串

    std::string_view sub = str.substr(0, 5);

❌ 不适合使用 string_view 的场景

  1. 需要拥有数据:需要修改或长期持有字符串

    std::string str = sv; // 需要拷贝
  2. 生命周期不确定:无法保证被引用的字符串存活

    std::string_view get_view() { /* 危险 */ }
  3. 需要空终止符:调用 C 风格 API

    printf("%s", sv.data()); // 危险
    printf("%s", std::string(sv).c_str()); // 安全
  4. 作为返回值:除非返回的是静态字符串或字符串池中的视图

性能建议

  • 优先使用 string_view 作为函数参数:零成本传递
  • 谨慎在容器中存储 string_view:必须管理好生命周期
  • 使用字符串池模式:集中管理字符串,避免悬空引用
  • 性能测试:在实际场景中测试,不要过早优化

Summarystring_view 是个好工具,但别滥用。如果你搞不清楚生命周期,就老老实实用 std::string。悬空指针比内存分配更糟糕。