在realloc扩容的内存上销毁string对象出现double free错误的原因问询
问题原因分析
你这种用法本质是对C++非平凡类型执行二进制内存拷贝,本身就属于未定义行为,std::string崩溃、std::vector看似正常只是不同类型的内部实现差异导致的表象。
为什么std::string会崩溃
绝大多数标准库的std::string实现都带有小字符串优化(SSO):当字符串长度小于阈值(通常是15/23字节,依平台而定)时,字符串内容会直接存储在std::string对象本身的内存空间中,不会额外申请堆内存。
此时std::string内部会持有一个指向自身内部缓冲区的指针,当你调用realloc时:
- 如果无法原地扩容,
realloc会把旧内存的二进制内容原样拷贝到新地址,然后释放旧内存 - 拷贝后的新
std::string对象里的内部指针,仍然指向已经被释放的旧内存地址的缓冲区,变成野指针 - 调用析构函数时会访问这个野指针,直接触发崩溃。
为什么std::vector看似正常
std::vector的内部实现非常纯粹,只有三个指向堆内存的指针(起始、已用末尾、容量末尾),所有元素数据都存储在额外申请的堆内存中,和vector对象本身的存储地址完全无关。
你把vector对象本身二进制拷贝到新地址后,三个指针仍然指向有效的堆内存,调用析构时会正常释放对应的堆空间,不会访问已经被释放的旧vector对象地址,因此看起来运行正常。但要注意:这仍然是未定义行为,只是刚好符合你的预期而已,C++标准不保证这种用法的合法性。
正确做法
realloc只能用于平凡可拷贝类型(即没有自定义构造、析构、拷贝构造函数的C风格类型),非平凡类型绝对不能直接用realloc迁移对象。
如果你需要自行管理异构内存池,扩容必须遵循以下流程:
- 调用
malloc新分配一块更大的内存 - 对旧内存中的每个对象,用移动构造函数 placement new 到新内存的对应位置
- 逐个调用旧内存中对象的析构函数
- 释放旧内存
没有简化的捷径,跳过任何一步都会产生未定义行为。
内容的提问来源于stack exchange,提问作者shubulan
相关产品推荐
相关产品推荐

