cpp字符串
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通过上面的代码发现有下面2点:
- 发现在size小于16的短小
string并没有调用new,而是直接分配在栈上的 - 使用s1来初始化s2的时候直接分配了内存,并没有使用
cow, cpp 高版本取消了cow
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;
}常用操作
#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_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 可以显著提高程序性能,特别是在需要频繁传递字符串参数但不需要修改字符串的场景中。





