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"
std::cout << sv1 << std::endl; // 输出: Hello World
std::cout << sv4 << std::endl; // 输出: Hello
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;
}
return 0;
}
与 std::string 的区别
特性 | std::string | std::string_view |
---|---|---|
数据拥有权 | 有 | 无 |
内存分配 | 可能需要 | 不需要 |
大小 | sizeof(std::string) | sizeof(指针+长度) |
修改能力 | 可修改 | 只读 |
性能 | 较低 | 较高 |
使用注意事项
- 生命周期: 必须确保被引用的字符串数据在 string_view 使用期间保持有效
- 只读: string_view 不允许修改底层数据
- 非空终止: string_view 不保证以空字符结尾
实际应用场景
// 函数参数:避免不必要的字符串复制
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 可以显著提高程序性能,特别是在需要频繁传递字符串参数但不需要修改字符串的场景中。