确定问题所在
首先,我们要通过说明每个语句执行后内存的状态来清楚地了解问题所在。
int main() {
auto p = (Person*)malloc(sizeof(Person));
p = new(p)Person();
p->name=Test1::MSG1;
free(p);
return 0;
}
正如您所看到的,调用free(p)
可以释放最初由malloc
分配的内存,但它不会释放在分配给p->name
时分配的内存。
这就是内存泄漏。
解决问题
将Person
对象放在堆上有两个方面:
- 内存分配——在这里由
malloc
/free
处理。
- 初始化和终止该内存——通过对构造函数和析构函数的调用来处理。
由于缺少对析构函数的调用,因此持有Person
的资源会被泄漏。这里是内存,但如果Person
持有锁,则可能出现永久锁定的互斥体等情况。因此,执行析构函数是必要的。
c风格的方法是自己调用析构函数:
int main() {
auto p = (Person*)malloc(sizeof(Person));
p = new(p) Person();
p->name = Test1::MSG1;
std::cout << "name: "<< p->name << "\n";
p->~Person();
free(p);
std::cout << "done" << "\n";
return 0;
}
然而,这不是符合C++惯用法的:容易出错等等...
C++的方法是使用RAII来确保当p
超出作用域时,所有资源都被正确处理:执行Person
的析构函数并且释放为Person
分配的内存。
首先,我们将创建一些辅助程序。我使用了c
命名空间,因为我不知道您使用的C库的名称,但我建议您更具体地说明:
namespace c {
struct Disposer<T> {
void operator()(T* p) {
p->~T();
free(p);
}
};
template <typename T>
using UniquePointer<T> = std::unique_ptr<T, Disposer<T>>;
template <typename T, typename... Args>
UniquePointer<T> make_unique(T* t, Args&&... args) {
try {
new (t) T(std::forward<Args>(args)...);
} catch(...) {
free(t);
throw;
}
return UniquePointer{t};
}
}
有了这个,我们可以改进原始的例子:
int main() {
auto raw = (Person*) malloc(sizeof(Person));
auto p = c::make_unique(raw);
p->name = Test1::MSG1;
std::cout << "name: "<< p->name << "\n";
std::cout << "done" << "\n";
return 0;
}
注意:不要使用
std::endl
,而应该使用
'\n'
或
"\n"
。
std::endl
除了加入换行符外,还调用
.flush()
,这很少是你想要的——它会减慢程序运行速度。
free
之前,您必须调用析构函数。 - HolyBlackCatmalloc
不会构造对象一样,free
也不会析构对象。 - Some programmer dude#include <string>
和#include <cstdlib>
。 - PaulMcKenzie<string>
和<cstdlib>
包含在<iostream>
中(标准既不要求也不阻止这样做)。 - Peteralignas
,@PaulSanders。因为std::malloc()
返回的内存已经适当地对齐,可以分配给任何具有基本对齐要求的对象指针(或空指针)。 - Toby Speight