使用malloc/free处理含std::string对象时内存泄漏原因及解决方法
内存泄漏原因及解决方法
问题代码
#include <iostream> namespace Test1 { static const std::string MSG1="Something really big message"; } struct Person{ std::string name; }; int main() { auto p = (Person*)malloc(sizeof(Person)); p = new(p)Person(); p->name=Test1::MSG1; std::cout << "name: "<< p->name << std::endl; free(p); std::cout << "done" << std::endl; return 0; }
Valgrind报错
definitely lost: 31 bytes in 1 blocks
泄漏原因
你用malloc分配了Person结构体的内存,这是C风格的内存分配,不会触发C++对象的构造逻辑。之后用placement new构造了Person对象,这会初始化内部的std::string name——std::string会自己分配堆内存来存储字符串数据。
但最后直接调用free(p)时,只会释放malloc分配的那块结构体内存,不会调用Person的析构函数。std::string的析构函数负责释放它内部持有的堆内存,没被调用的话,这块内存就永远无法回收,导致内存泄漏。
解决方案
基础方案:手动调用析构函数
在free(p)之前,显式调用Person的析构函数,确保std::string的内部内存被释放:
#include <iostream> namespace Test1 { static const std::string MSG1="Something really big message"; } struct Person{ std::string name; }; int main() { auto p = (Person*)malloc(sizeof(Person)); p = new(p)Person(); p->name=Test1::MSG1; std::cout << "name: "<< p->name << std::endl; // 手动调用析构,释放std::string内部内存 p->~Person(); // 释放malloc分配的结构体内存 free(p); std::cout << "done" << std::endl; return 0; }
适合反复赋值的优化方案
如果需要反复给name赋值,甚至多次复用这块内存构造Person对象,可以封装一个简单的管理类,自动处理析构逻辑,避免手动操作出错:
#include <iostream> #include <cstdlib> namespace Test1 { static const std::string MSG1="Something really big message"; } struct Person{ std::string name; }; // 封装malloc分配的Person,自动管理生命周期 struct PersonHolder { Person* ptr; PersonHolder() : ptr(static_cast<Person*>(malloc(sizeof(Person)))) { new(ptr)Person(); // 初始化对象 } ~PersonHolder() { ptr->~Person(); // 自动调用析构 free(ptr); // 释放底层内存 } // 禁止拷贝,防止double free PersonHolder(const PersonHolder&) = delete; PersonHolder& operator=(const PersonHolder&) = delete; // 提供箭头访问,和原生指针用法一致 Person* operator->() { return ptr; } const Person* operator->() const { return ptr; } }; int main() { PersonHolder holder; holder->name = Test1::MSG1; std::cout << "name: "<< holder->name << std::endl; // 直接反复赋值name,无需手动管理内存 holder->name = "Updated message"; std::cout << "updated name: "<< holder->name << std::endl; // 甚至可以复用内存:先析构再重新构造 holder.ptr->~Person(); new(holder.ptr)Person(); holder->name = "Reconstructed name"; std::cout << "reconstructed name: "<< holder->name << std::endl; // 程序结束时,PersonHolder的析构函数会自动完成清理 std::cout << "done" << std::endl; return 0; }
核心要点
- 用
malloc分配的内存存储C++对象时,必须在free前手动调用对象的析构函数,否则所有含内部动态内存的成员(比如std::string、std::vector)都会泄漏。 - 如果需要复用内存,先调用析构函数销毁当前对象,再用placement new重新构造新对象,之后可以继续赋值成员变量。
内容的提问来源于stack exchange,提问作者Anurag Vohra
相关产品推荐
相关产品推荐

