当前位置: 首页 > news >正文

C++17 新特性: std::string_view —— 减少内存分配,让std::string运行得更快

在 C++ 中,字符串操作看似简单,但在性能敏感的场景下,频繁的堆内存分配可能会成为性能瓶颈。C++17 引入了 std::string_view,可以让我们以零拷贝的方式访问字符串数据,从而显著提升性能。本文将通过实际代码演示问题、验证方法和解决方案。


一、原始方法的问题:std::string 会隐式分配内存

在 C++ 中,std::string 会在构造时分配内存。例如:

#include <iostream>
#include <string>void PrintName(const std::string& name) {std::cout << "Name: " << name << std::endl;
}int main() {std::string name = "Hatsune Miku";PrintName(name);
}

即便是传入字面量 "Hatsune Miku"std::string 也会在堆上分配内存来存储字符串。

如果我们要拆分名字和姓氏:

std::string firstName = name.substr(0, 7);
std::string lastName  = name.substr(8, 4);PrintName(firstName);
PrintName(lastName);

每次 substr() 都会触发新的堆内存分配。频繁操作时,这种隐式分配可能成为性能瓶颈。

注意:现代 C++ 实现对小字符串使用 SSO(Small String Optimization)
对于短字符串(通常 < 15~23 字符),std::string 会直接在栈上存储,不会触发堆分配
所以对于 "Hatsune Miku" 这种短字符串,即使调用 substr(),堆分配计数仍可能是 0。


二、如何验证内存分配:重载 operator new

为了直观观察程序中的堆内存分配,可以重载全局 operator new

static uint32_t s_AllocCount = 0;void* operator new(size_t size) {s_AllocCount++;std::cout << "Allocating " << size << " bytes\n";return malloc(size);
}

使用这个方法,我们可以统计堆分配次数:

std::string name = "Hatsune Miku";
std::string firstName = name.substr(0, 7);
std::string lastName  = name.substr(8, 4);PrintName(firstName);
PrintName(lastName);std::cout << s_AllocCount << " allocations totally\n";

输出示例:

Allocating 16 bytes
Allocating 16 bytes
Allocating 16 bytes
3 allocations totally

可以看到,每个 std::string 构造和 substr() 都触发了一次分配,说明原始方法确实会带来额外开销。


三、std::string_view 的主要特性

  • 非拥有所有权std::string_view 不管理字符串的内存,不负责分配或释放。

  • 不可变性:视图中字符串内容不可被修改,保证数据安全性。

  • 高效性:无需内存拷贝,传递和操作字符串更加高效。

  • 灵活性:可以从多种类型的字符串对象构造,如 std::string、C 字符串等。

四、与 std::string 的区别

特性std::stringstd::string_view
所有权管理拥有底层字符串的所有权不拥有底层字符串的所有权
可变性可修改字符串内容不可修改字符串内容
内存拷贝通常涉及内存拷贝无需内存拷贝
构造成本高(涉及内存分配和拷贝)低(仅记录指针和长度)
生命周期管理管理自身资源依赖外部字符串的生命周期
使用场景需要拥有或修改字符串时只需读取或传递字符串时

理解这些区别有助于选择合适的数据类型,提高性能并减少不必要的堆分配。

五、使用 std::string_view 避免不必要分配

std::string_view 是一个轻量级对象,只包含一个指针和长度,并不拥有字符串本身。它可以安全地创建字符串“窗口”,而无需在堆上分配内存。

#include <iostream>
#include <string>
#include <string_view>void PrintName(std::string_view name) {std::cout << "Name: " << name << std::endl;
}int main() {std::string name = "Hatsune Miku";std::string_view firstName(name.c_str(), 7);   // 指向 "Hatsune"std::string_view lastName(name.c_str() + 8, 4); // 指向 "Miku"PrintName(firstName);PrintName(lastName);std::cout << s_AllocCount << " allocations totally\n";
}

输出结果:

Name: Hatsune
Name: Miku
1 allocations totally

这里仅在 name 初始化时发生一次分配,子字符串操作不再分配内存。

如果字符串本身是静态字面量,则可以做到完全零分配:

const char* name = "Hatsune Miku";std::string_view firstName(name, 7);
std::string_view lastName(name + 8, 4);PrintName(firstName);
PrintName(lastName);

输出结果:

Name: Hatsune
Name: Miku
0 allocations totally

字符串在静态存储区,string_view 仅是指针 + 长度,整个过程没有堆内存分配。

在这种情况下,程序 没有任何堆内存分配,效率最优。


六、总结

  • 问题根源std::string 拥有自己的内存,每次构造或 substr() 都会分配内存。

  • 验证方法:重载 operator new 可以统计堆内存分配次数。

  • 解决方案std::string_view 提供对已有字符串的只读窗口,避免不必要的分配。

  • 最佳实践

    • 函数参数尽量使用 std::string_view,减少隐式构造。

    • 静态字符串使用 const char* + std::string_view 可做到零分配。

    • 避免频繁使用 substr(),用 string_view 截取子串更高效。

通过这种方式,我们不仅写出可读性强的代码,也显著减少了内存分配开销,提升程序性能。

http://www.dtcms.com/a/461610.html

相关文章:

  • 北京营销策划有限公司优化官方网站设计
  • 网站建设涉及和描述的一些问题珠海网站建设厚瑜
  • 11. ubuntu14.0.4 安装文件管理器右键打开终端
  • k8s架构组件
  • 「机器学习笔记8」决策树学习:从理论到实践的全面解析(下)
  • ES6(二)
  • 做co的网站学校网页设计模板图片
  • QTreeView实现多折叠效果
  • 纯 flash 网站比较好的设计欣赏网站
  • 【笔记】树链剖分三题(洛谷 P3384 树剖模板 P2146 软件包管理器 P2486 染色)
  • 建设银行网站用户名忘了怎么办wordpress标签链接优化
  • 文献阅读:A Survey of Edge Caching: Key Issues and Challenges
  • 信号140上岸山东师范经验。
  • 力扣面试经典150题day1第一题(lc88),第二题(lc27)
  • asp 网站开发 软件做期货主要看哪几个财经网站
  • JavaScript实现防抖、节流【带思路】
  • 汇川高压变频器故障解析F79 F90
  • kanass入门到实战(13) - 如何通过评审,有效保障需求和用例的质量
  • 深度解析:Redis缓存三大核心问题(穿透/击穿/雪崩)的技术原理与企业级解决方案
  • 最专业网站建设哪家好微网站微名片
  • 上海兆越通讯闪耀第二十五届中国国际工业博览会
  • 车库到双子星:惠普的百年科技传奇
  • 网站防止恶意注册dedecms菜谱网站源码
  • 基于IoT的智能温控空调系统设计与实现
  • 网站开发常用的框架营销到底是干嘛的
  • 老题新解|组合数问题
  • Java 工具类详解:Arrays、Collections、Objects 一篇通关
  • Cucumber自学导航
  • docker案例
  • 网站如何做提现功能上海市城乡和住房建设厅网站