- 此问题在此处已有答案**:
How to properly free the memory allocated by placement new?(5个答案)
昨天关门了。
这是我在实际代码中遇到的问题的最小工作示例。
#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运行它时,它给我这个错误:
明确丢失:1个数据块中31个字节
限制
1.在上面的例子中,我必须使用malloc
,因为在我的真实代码中,我在C++项目中使用了一个C库,它在内部使用了malloc
,所以我无法摆脱malloc
的使用,因为我在代码中没有明确地使用它。
1.我需要在代码中一次又一次地重新分配Person
的std::string name
。
5条答案
按热度按时间mlnl4t2r1#
你代码中的重要部分一行一行的...
为一个Person对象分配内存:
通过调用Person对象的构造函数,在已分配的内存中构造Person对象:
释放通过malloc分配的内存:
通过放置
new
调用构造函数会创建一个std::string
。该字符串会在析构函数中被销毁,但析构函数永远不会被调用。free
不会调用析构函数(就像malloc
不会调用构造函数一样)。malloc
只分配内存。Placement new只在已经分配的内存中构造对象。因此,在调用free
之前,您需要调用析构函数。这是我所知道的唯一一种情况,在这种情况下,显式调用析构函数是正确的和必要的:gxwragnw2#
必须在
free(p);
之前手动调用析构函数:或者
std::destroy_at(p)
,这是一回事。wmomyfyw3#
找出问题所在
首先,让我们通过说明每个语句之后的记忆状态来明确到底是什么问题。
正如您所看到的,调用
free(p)
释放了最初由malloc
分配的内存,但是它没有释放由p->name
分配的内存。这是你的漏洞
解决问题
在堆上拥有
Person
对象有两个方面:malloc
/free
处理。你缺少对析构函数的调用,因此
Person
的资源被泄露了,这里是内存,但是如果Person
持有锁,你可能会有一个永久锁定的互斥锁,等等...因此执行析构函数是必要的。C风格的方法是自己调用析构函数:
然而这不是惯用的C++:容易出错等等。
C++的方法是使用RAII来确保当
p
超出作用域时,其所有资源都被正确处理:执行X1 M9 N1 X的析构函数 * 并且 * 释放为X1 M10 N1 X本身分配的存储器。首先,我们要创建一些helper,我使用了
c
命名空间,因为我不知道你使用的C库的名称,但是我请你更具体地说:这样,我们就可以改进原来的例子:
std::endl
,而是使用'\n'
或"\n"
。std::endl
在放置行尾之前调用.flush()
,这通常不是您想要的--它会减慢速度。*sqxo8psd4#
正如在其他答案中提到的,泄漏的来源是
Person
的name
成员的析构函数没有被调用。通常在调用Person
的析构函数时会隐式调用它。但是,Person
从未被析构。Person
示例的内存只是随free
释放。因此,正如必须显式调用
new
后的构造函数一样,也需要显式调用free
前的析构函数。您还可以考虑重载
new
和delete
运算符。这样,您可以正常使用
new
和delete
,而在下面,它们将使用malloc
和free
。这样,您可以更自然地使用智能指针。
当然,您可以使用带有定制删除器的
unique_ptr
来显式调用malloc
和free
,但是这样做会麻烦得多,并且您的删除器还需要知道显式调用析构函数。nwlqm0z15#
正如其他人所提到的,由
Person
的成员分配的动态内存只能由析构函数~Person
释放,而free()
不会调用析构函数。如果你必须在一个需要初始化和清理的库中使用这个函数,比如这里,一种方法是定义一个新的deleter,供标准库智能指针用途:即使您没有分配自己的内存块,这也可以正常工作。
这使您可以安全地用途:
使用自动内存管理。你可以从函数返回智能指针,当它的生存期结束时,析构函数和
free()
都会被调用。你也可以用这种方法创建一个std::shared_ptr
。如果出于某种原因,你需要在智能指针还活着的时候销毁对象,你可以reset
或release
它。