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

使用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 14:53:23