You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何调用10000次std::string传值与传引用分配次数相同?

关于std::string传值复制时无重复堆分配的疑问

测试代码

void    ref(std::string& str)
{ (void)str; }
void    copy(std::string str)
{ (void)str; }

int main()
{
    std::string str = "Hello World";
    for(size_t i = 0; i < 10000; i++)
        ref(str);
}

问题描述

调用10000次ref(str)(传引用)或copy(str)(传值)时,堆分配次数完全相同。按常理复制对象应为新对象分配新内存空间,但valgrind显示两种调用的堆总分配均为1次分配、1次释放,共72704字节,并未出现10000次分配。想请教:std::string是如何在函数传值复制对象时不重复分配内存的?为何没有产生10000次分配?


解答

核心原因:写时复制(Copy-On-Write, COW)机制

你看到的现象源于早期C++标准库std::string普遍采用的写时复制优化策略:

  • 当传值复制std::string时,新对象不会立即在堆上分配内存复制字符数据,而是与原对象共享同一块底层内存,同时通过引用计数记录共享该内存的对象数量。
  • 只有当任意一个共享内存的对象尝试修改字符串内容时,才会触发真正的堆内存分配和数据复制,这就是“写时复制”的核心逻辑。

你的copy函数仅对参数做了无意义的类型转换((void)str),完全没有修改字符串的操作,因此10000次传值复制都不会触发新的堆分配——所有临时std::string对象都共享原str的内存,最终只会存在原对象的1次堆分配与释放。

补充:C++11后的标准库变化

C11发布后,由于写时复制在多线程场景下存在性能瓶颈与线程安全隐患,主流标准库实现(如Clang的libc、GCC高版本的libstdc++)已逐步放弃COW,转而采用小字符串优化(SSO):

  • 对于长度较短的字符串(通常为15字节以内,不含终止符),直接将字符数据存储在std::string对象的栈空间中,完全不涉及堆分配;
  • 仅当字符串长度超过阈值时,才会在堆上分配内存。

你的valgrind结果显示存在1次堆分配,说明测试环境使用的是仍保留COW机制的std::string实现。


内容的提问来源于stack exchange,提问作者Maxou

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 18:32:52