前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >为什么std::string_view能解决std::string和char*的性能瓶颈?

为什么std::string_view能解决std::string和char*的性能瓶颈?

作者头像
程序员的园
发布2025-01-09 18:54:42
发布2025-01-09 18:54:42
6800
代码可运行
举报
运行总次数:0
代码可运行

日常开发中,字符串处理是最常见操作之一。C++提供了std::stringchar*两种字符串类型。然而,在某些场景下,它们可能会带来性能问题或设计上的局限性。为了解决这些问题,C++17 引入了 std::string_view

背景

std::stringchar*存在瑕疵,才引入的std::string_view。那std::string_view解决了std::stringchar*的什么问题呢

  • 不必要的内存复制:当 std::string 被传递给函数时,通常会发生一次深拷贝操作,即复制整个字符串内容。这一操作对于较大的字符串来说,可能会导致显著的性能开销。
  • 频繁的内存分配与释放:当字符串的内容发生修改时,std::string 可能会重新分配内存以适应新的内容,这种重新分配会带来额外的性能开销。
  • char* 的安全性问题char* 本身并不包含有关字符串长度的任何信息,因此开发人员必须依赖字符串结尾的空字符('\0')来确定字符串的结束位置。这种做法容易引发字符串越界、内存访问错误等问题。

代码示例

代码语言:javascript
代码运行次数:0
复制
#include <iostream>
#include <string>

//1. 函数传参
void need_copy_with_non_const_string(std::string str) { 
    std::cout << "Processing: " << str << std::endl;
}

//2. std::string 会触发内存的重新分配
int need_realloc 
{
    std::string str = "Initial String";
    str += " with more data"; // 修改字符串,可能导致重新分配内存
    std::cout << str << std::endl;
    return0;
}

int main() {
    std::string large_str = "This is a large string that might be copied.";
    need_copy_with_non_const_string(large_str); // 传递字符串时,发生了复制

    need_realloc(); // 修改字符串,可能导致重新分配内存
    return0;
}

注意: 在上述代码中,当 std::string 被传递给 process_string 函数时,整个字符串的数据会被复制到该函数的局部变量中。 当然,该问题并非不可解,可以通过以下方式进行优化:

  • 使用引用传递:通过引用传递字符串,可以避免不必要的复制。
  • 使用 const char* 传递:使用 const char* 作为参数类型,可以避免不必要的复制。

std::string_view

std::string_view 作为 C++17 引入的一种轻量级的新型字符串视图类,仅持有一个指向字符串数据的指针和一个表示字符串长度的整数。其具有如下优势:

  • 避免不必要的复制:尤其是当需要传递字符串时,std::string_view 避免了不必要的内存复制,提高了性能。
  • 避免内存分配与释放std::string_view 避免了内存分配与释放,减少了内存开销。
  • 增强安全性std::string_view 提供了字符串的长度信息,避免了字符串越界问题。

具体代码示例如下:

代码语言:javascript
代码运行次数:0
复制
//1. 函数传参
#include <iostream>
#include <string_view>

void process_string(std::string_view str) { // 传递字符串视图,不复制字符串
    std::cout << "Processing: " << str << std::endl;
}

int main() {
    std::string large_str = "This is a large string that might be copied.";
    process_string(large_str); // 直接传递视图,避免复制
    return0;
}

//2. 避免内存分配与释放
int main() {
    constchar* cstr = "This is a C-string";
    std::string_view view(cstr); // 创建字符串视图,避免内存分配
    std::cout << "String View: " << view << std::endl;
    return0;
}

//3. 增强安全性
int main() {
    constchar* cstr = "Hello, World!";
    std::string_view view(cstr); // 使用 string_view,避免了 char* 的长度问题
    std::cout << "String View: " << view << ", Length: " << view.size() << std::endl;
    return0;
}

常用接口

std::string_view 提供了许多有用的接口,以下是其中一些常用的接口:

代码语言:javascript
代码运行次数:0
复制
// 构造函数
std::string_view(constchar* str, size_t count); // 从字符数组创建
std::string_view(conststd::string& str); // 从 std::string 创建

// 成员函数
size_t size() const noexcept; // 返回字符串长度
size_t length() const noexcept; // 返回字符串长度
bool empty() const noexcept; // 判断字符串是否为空
const char* data() const noexcept; // 返回指向字符串数据的指针
cost_reference operator[](size_t pos) const; // 访问指定位置的字符
const_reference at(size_t pos) const; // 访问指定位置的字符,带边界检查
const_reference front() const; // 返回第一个字符
const_reference back() const; // 返回最后一个字符
void remove_prefix(size_t n); // 移除前 n 个字符
void remove_suffix(size_t n); // 移除后 n 个字符
int compare(std::string_view other) const noexcept; // 比较两个 string_view

//查找一族
size_t find(char ch, size_t pos = 0) const noexcept; // 查找字符 ch
size_t find(const char* str, size_t pos = 0) const noexcept; // 查找字符串 str
size_t find(std::string_view str, size_t pos = 0) const noexcept; // 查找字符串 str
size_t rfind(char ch, size_t pos = npos) const noexcept; // 从后向前查找字符 ch
size_t find_first_of(char ch, size_t pos = 0) const noexcept; // 查找第一个匹配的字符
size_t find_first_not_of(char ch, size_t pos = 0) const noexcept; // 查找第一个不匹配的字符
size_t find_last_of(char ch, size_t pos = npos) const noexcept; // 从后向前查找第一个匹配的字符
size_t find_last_not_of(char ch, size_t pos = npos) const noexcept; // 从后向前查找第一个不匹配的字符
bool _Starts_with(std::string_view prefix) constnoexcept; // 判断是否以 prefix 开头

注意事项

尽管 std::string_view 提供了许多优势,但在使用时仍然需要小心。string_view 并不负责管理其所指向的数据的生命周期,因此string_view 存续期间其持有的字符串的有效性需要开发者自行保证。

代码语言:javascript
代码运行次数:0
复制
#include <iostream>
#include <string_view>

void print_view(std::string_view view) {
    std::cout << "String View: " << view << std::endl;
}

int main() {
    std::string str = "Hello, World!";
    std::string_view view(str); // 从 std::string 创建 view

    str.clear(); // 清空 std::string
    print_view(view); // 此时 view 变为悬空指针,未定义行为
    return0;
}

在上面的代码中,std::string_view 引用的 std::string 被清空后,std::string_view 变成了一个悬空指针,访问它将导致未定义行为。因此,在使用 std::string_view 时,必须确保其引用的原始数据在整个生命周期内有效。

总结

std::string_view 作为 C++17 引入的一个新特性,极大地优化了字符串处理的性能,尤其是在频繁传递和操作字符串时。通过避免不必要的内存复制和分配,std::string_view 提供了一种高效的方式来操作字符串数据。然而,std::string_view 不负责内存管理,使用时需要小心数据的生命周期和悬空指针问题。通过合理运用 std::string_view,可以在确保性能的同时,提高程序的安全性和灵活性。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2025-01-08,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 程序员的园 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 背景
  • std::string_view
    • 常用接口
  • 注意事项
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档