std::string_view
是C++17引入的一个轻量级、非拥有的字符串视图类,定义于
<string_view>
头文件中。它提供了一种高效且安全的方式来处理字符串数据,而无需复制字符串内容
1、 基本概念
std::string_view
本质上是对一个字符串序列的引用,它并不拥有字符串数据,只是持有指向字符串数据的指针和长度信息。这使得它在处理字符串时避免了不必要的内存分配和复制操作,提高了程序的性能。
主要用途
- 函数参数传递:当函数需要处理字符串但不需要拥有字符串所有权时,可以使用
std::string_view
作为参数,这样可以避免不必要的字符串复制。 - 临时字符串处理:在处理临时字符串或者子字符串时,使用
std::string_view
可以避免创建新的std::string
对象,从而提高效率。
示例代码
以下是一个简单的示例,展示了 std::string_view
的基本用法:
#include <iostream>
#include <string_view>
// 接受 std::string_view 作为参数的函数
void printStringView(std::string_view sv) {
std::cout << "String view content: " << sv << std::endl;
}
int main() {
// 从 C 风格字符串创建 std::string_view
const char* cstr = "Hello, World!";
std::string_view sv1(cstr);
// 从 std::string 创建 std::string_view
std::string str = "Hello, C++!";
std::string_view sv2(str);
// 调用函数打印 std::string_view 内容
printStringView(sv1);
printStringView(sv2);
return 0;
}
代码解释
- 包含头文件:包含
<iostream>
用于输入输出,<string_view>
用于使用std::string_view
。 - 定义函数:定义了一个接受
std::string_view
作为参数的函数printStringView
,用于打印字符串视图的内容。 - 创建
std::string_view
:- 从 C 风格字符串创建
std::string_view
:std::string_view sv1(cstr);
- 从
std::string
创建std::string_view
:std::string_view sv2(str);
- 从 C 风格字符串创建
- 调用函数:调用
printStringView
函数打印字符串视图的内容。
注意事项
- 生命周期管理:由于
std::string_view
不拥有字符串数据,它所引用的字符串数据必须在std::string_view
的生命周期内保持有效。否则,访问std::string_view
可能会导致未定义行为。 - 不可修改性:
std::string_view
是只读的,不能直接修改其内容。如果需要修改字符串,应该使用std::string
。
常用成员函数
length()
或size()
:返回字符串视图的长度。data()
:返回指向字符串数据的指针。substr()
:返回一个新的std::string_view
,表示原字符串视图的子字符串。starts_with()
和ends_with()
:检查字符串视图是否以指定的前缀或后缀开头或结尾。find()
:查找指定子字符串或字符的第一次出现位置。
以下是一个使用这些成员函数的示例:
#include <iostream>
#include <string_view>
int main() {
std::string_view sv = "Hello, World!";
// 获取字符串视图的长度
std::cout << "Length: " << sv.length() << std::endl;
// 获取子字符串
std::string_view sub = sv.substr(0, 5);
std::cout << "Substring: " << sub << std::endl;
// 检查前缀
if (sv.starts_with("Hello")) {
std::cout << "Starts with 'Hello'" << std::endl;
}
// 查找字符
size_t pos = sv.find(',');
if (pos != std::string_view::npos) {
std::cout << "Comma found at position: " << pos << std::endl;
}
return 0;
}
通过使用 std::string_view
,可以在不复制字符串数据的情况下高效地处理字符串,从而提高程序的性能和效率。
2、对比string
在实际应用里,std::string_view
相较于 std::string
存在不少优势,下面从性能、灵活性和安全性等方面展开详细介绍。
性能优势
避免内存分配与复制
std::string
会拥有字符串数据,在创建、赋值或者传递时,通常需要进行内存分配与数据复制操作。而 std::string_view
只是对已有字符串数据的引用,不拥有数据,所以不会产生额外的内存分配和复制开销。
下面是一个简单示例,能体现出两者在性能上的差异:
#include <iostream>
#include <string>
#include <string_view>
#include <chrono>
// 使用 std::string 作为参数的函数
void processString(const std::string& str) {
// 模拟处理操作
std::cout << str.length() << std::endl;
}
// 使用 std::string_view 作为参数的函数
void processStringView(std::string_view sv) {
// 模拟处理操作
std::cout << sv.length() << std::endl;
}
int main() {
const char* cstr = "This is a long string for testing performance.";
// 测试 std::string 的性能
auto start1 = std::chrono::high_resolution_clock::now();
for (int i = 0; i < 1000000; ++i) {
processString(cstr);
}
auto end1 = std::chrono::high_resolution_clock::now();
auto duration1 = std::chrono::duration_cast<std::chrono::milliseconds>(end1 - start1).count();
// 测试 std::string_view 的性能
auto start2 = std::chrono::high_resolution_clock::now();
for (int i = 0; i < 1000000; ++i) {
processStringView(cstr);
}
auto end2 = std::chrono::high_resolution_clock::now();
auto duration2 = std::chrono::duration_cast<std::chrono::milliseconds>(end2 - start2).count();
std::cout << "Time taken by std::string: " << duration1 << " ms" << std::endl;
std::cout << "Time taken by std::string_view: " << duration2 << " ms" << std::endl;
return 0;
}
在这个示例中,processString
函数接收 std::string
类型的参数,每次调用时都会进行字符串复制;而 processStringView
函数接收 std::string_view
类型的参数,不会进行复制操作。通过计时可以发现,使用 std::string_view
能显著提升性能。
轻量级数据结构
std::string_view
一般只包含一个指向字符串数据的指针和一个长度信息,其内存开销较小。相比之下,std::string
除了存储字符串数据外,还需要额外的空间来管理内存,如存储容量信息、分配器等。
灵活性优势
支持多种字符串源
std::string_view
可以从多种类型的字符串源创建,包括 std::string
、C 风格字符串(const char*
)以及字符数组等。这使得它在处理不同类型的字符串时更加灵活。
#include <iostream>
#include <string>
#include <string_view>
void printStringView(std::string_view sv) {
std::cout << sv << std::endl;
}
int main() {
std::string str = "Hello, std::string!";
const char* cstr = "Hello, C-style string!";
char arr[] = {'H', 'e', 'l', 'l', 'o', '\0'};
printStringView(str);
printStringView(cstr);
printStringView(arr);
return 0;
}
方便进行子字符串操作
std::string_view
提供了 substr
等成员函数,能够方便地获取原字符串的子字符串,而且不会复制子字符串的数据,效率较高。
#include <iostream>
#include <string_view>
int main() {
std::string_view sv = "Hello, World!";
std::string_view sub = sv.substr(0, 5);
std::cout << sub << std::endl;
return 0;
}
安全性优势
只读特性
std::string_view
是只读的,不能直接修改其内容。这有助于避免意外修改字符串数据,提高程序的安全性。如果需要修改字符串,应该使用 std::string
。
#include <iostream>
#include <string_view>
int main() {
std::string_view sv = "Read-only string";
// 以下代码会导致编译错误,因为 std::string_view 是只读的
// sv[0] = 'W';
return 0;
}
综上所述,在只需要对字符串进行读取操作、不需要拥有字符串所有权的场景中,使用 std::string_view
可以提高程序的性能和灵活性,同时增强代码的安全性。