我试图了解在由
malloc()
分配的缓冲区中,对
placement new
的结果调用
free()
是否有效。
请考虑以下几点。这段代码是否表现出任何未定义的行为?
(假设
T
没有重载任何
new
或
delete
操作符)
T* create() {
void* buf = malloc(sizeof(T) + 10);
T* obj = new(buf) T;
返回 obj;
}
int main() {
T* obj = create();
obj->~T();
free(obj); // or free(static_cast<void*>(obj)
}
由
malloc()
编辑的缓冲区上的
placement new
所返回的指针是否可以传递给
free()
呢?如果我们能保证
static_cast<void*>(obj) == buf
,这应该是很好定义的,但我不确定是否真的能保证这种平等;
placement new
操作符
返回其参数不变,但我不确定是否允许我们假设
placement new
表达式
返回一个具有相同值/指向相同地址的指针。
如果没有定义这一点,那么是否有其他方法可以获得一个指针,该指针既可用于访问对象,也可用于释放对象,而不会遇到未定义的行为?
你在代码中观察到的行为是
未定义的
。虽然表面上看起来应该可以,但 C++ 标准并没有保证
placement new
表达式的结果与其第一个参数完全相同。
让我们来分解一下原因:
-
placement new
的作用:placement new
运算符的作用是在内存的特定位置构造一个对象。它接受一个指向原始内存的指针作为参数,并在该位置构造对象。 关键在于,它返回的指针并不保证与传递给它的指针完全相同。 -
C++ 标准的内容: C++ 标准 ( §6.7.4.2/2 ) 指出,
placement new
表达式的结果是“指向已构造对象的指针”。它没有明确说明该指针必须与传递给placement new
的指针完全相同。 -
编译器优化: 尽管标准没有强制要求,但一些编译器可能会优化
placement new
表达式,使其返回与输入指针相同的地址。但这只是一个实现细节,不能依赖它。 -
free()
的要求:free()
函数需要传递给它的指针必须是之前由malloc()
、calloc()
或realloc()
返回的指针。传递任何其他指针会导致未定义的行为。
那么,如何安全地释放内存呢?
为了安全地释放内存,你需要跟踪原始指针(即传递给
malloc()
的指针)。你可以这样做:
T* create() {
void* buf = malloc(sizeof(T) + 10);
T* obj = new(buf) T;
return static_cast<T*>(buf); // 返回原始指针
}
int main() {
T* buf = create(); // 存储原始指针
T* obj = reinterpret_cast<T*>(buf); // 获取对象的指针
obj->~T(); // 调用析构函数
free(buf); // 释放原始指针指向的内存
}
在这个例子中,我们存储了
malloc()
返回的原始指针
buf
。然后,我们将它 reinterpret_cast 为
T*
来访问对象。最后,我们使用原始指针
buf
来释放内存。
其他选择:
-
使用标准容器:
标准容器,如
std::vector
,可以为你管理内存分配和释放。 - 使用自定义分配器: 如果你需要更精细的内存管理控制,你可以创建自定义分配器。
总而言之,虽然你的代码看起来合理,但它依赖于 C++ 标准没有保证的行为。为了确保你的代码是可移植和安全的,最好跟踪原始指针并使用它来释放内存。
标签:c++,c++14,language-lawyer From: 78589716