在不先显式调用析构函数的情况下,在旧对象上使用placement new是否危险
Is it dangerous to use placement new on an old object without explicitly calling the destructor first?
我想为对象回收内存,而不是释放和重建它。假设Foo
在实践中不包含指针(但可能包含函数),下面使用"placement new"是否安全?
此外,最后的delete
调用是否安全,它是否会正确地调用第二个"新"对象上的析构函数,并在之后正确地释放内存?
#include <new>
struct Foo {
int hello;
int world;
};
int main() {
Foo* foo = new Foo;
// Do something with foo
// Done with foo, writing a new version of foo on top of the old one.
new(foo) Foo();
delete(foo);
}
上面这个简单的例子编译和运行都没有错误,但我无法通过运行它来判断它是否会在更复杂的环境中因某种原因而崩溃。
它是安全的,因为您覆盖的对象有一个微不足道的析构函数。来自n3337,第3.8章(对象生存期):
4程序可以通过重用对象所占用的存储或显式地终止任何对象的生命周期使用非平凡的析构函数为类类型的对象调用析构函数对于类类型的对象对于非平凡的析构函数,程序不需要在存储之前显式调用析构函数物体所占据的空间被重新使用或释放;但是,如果没有显式调用析构函数,或者如果delete表达式(5.3.5)不用于释放存储,则不应隐式调用析构函数,并且任何依赖于析构函数产生的副作用的程序都有未定义的行为。
delete
呼叫也是安全的。你在从new
得到的指针上调用它,在那个地方有一个活动对象。
正如你在问题中暗示的那样,如果析构函数是非平凡的并且有副作用,它可能会调用未定义的行为——在这种情况下,你需要显式地调用它。类是否包含指针并不直接重要——即使在这种情况下,重用存储也是安全的,但当然,这样可能会引入内存泄漏和其他错误。
不,如果您做得正确,重用对象的内存并不危险。此外,您不必将自己限制在没有指针的对象上:通过显式调用析构函数,您可以准备重用对象,如下所示:
Foo* foo = new Foo;
// Do something with foo
// Done with foo, writing a new version of foo on top of the old one.
foo->~Foo(); // Call the destructor explicitly to clean up the resources of a Foo
new(foo) Foo(); // Place new data into the previously allocated memory
delete(foo); // We are deleting a fully initialized object, so it is OK
已经有两个答案了,但我担心它们给出了一个不完整的画面。
您可以重用对象的存储,前提是您尊重以下几个条件:
- 您不需要使用动态分配的对象,任何对象都可以
- 您应该通过(显式)调用其析构函数来正确地销毁前一个对象;如果析构函数有副作用,则不这样做会导致未定义的行为(参见§3.8/4)
- 放置的对象应具有与上一个对象相同的动态类型(请参见§3.8/7)
让我们回顾一下,从开始,任何对象都可以:
struct Foo {
int hello;
int world;
};
void automatically_allocated() {
Foo foo;
foo.~Foo();
new (&foo) Foo{};
}
void dynamically_allocated() {
std::unique_ptr<Foo> foo(new Foo{});
foo->~Foo();
new (&*foo) Foo{};
}
继续使用销毁前一个对象:
struct Bar {
int hello;
std::string world;
};
void UNDEFINED_BEHAVIOR() {
Bar bar;
new (&bar) Bar{}; // most likely scenario: leaks memory owned by bar.world
}
最后使用相同的动态类型:
struct Base { virtual ~Base() {} };
struct Derived: Base { std::string world; };
struct Other: Base { int hello; }
void UNDEFINED_BEHAVIOR() {
Derived derived;
Base& b = derived;
b.~Base(); // fine
new (&b) Other{};
// Most likely here, calls "derived.~Derived()" on an object of type Other...
}
- 什么时候调用组成单元对象的析构函数
- 对RValue对象调用的LValue ref限定成员函数
- CMake-按正确顺序将项目与C运行时对象文件链接
- 空基优化子对象的地址
- 将对象数组的引用传递给函数
- 你能重载对象变量名本身返回的内容吗
- C++使用整数的压缩数组初始化对象
- 找不到成员对象:没有名为get_event()的成员,也处理多态性和向量
- 将对象移动到std::shared_ptr
- 代理对象的常量正确性
- 提升 ASIO 无法识别计时器对象
- 将Ref对象作为类成员
- 将包含C样式数组的对象初始化为成员变量(C++)
- 有没有一种方法可以使用placement new将堆叠对象分配给分配的内存
- 在不先显式调用析构函数的情况下,在旧对象上使用placement new是否危险
- 删除使用placement new分配的动态多态对象
- 从动态创建的数组构造pod对象时,是否推荐使用placement new ?
- 你能/如何重新分配用placement new创建的c++对象?
- 我是否需要删除一个用new和placement构造的对象
- 是否可以将堆栈对象地址赋给placement new