Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/154.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/actionscript-3/6.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 在旧对象上使用placement new而不先显式调用析构函数是否危险?_C++_Placement New - Fatal编程技术网

C++ 在旧对象上使用placement new而不先显式调用析构函数是否危险?

C++ 在旧对象上使用placement new而不先显式调用析构函数是否危险?,c++,placement-new,C++,Placement New,我想为一个对象回收内存,而不是释放和重建它。假设Foo实际上不包含指针(但可能包含函数),下面使用“placement new”是否安全 另外,最后的delete调用是否安全,它是否会正确地调用第二个“new”对象上的析构函数,并在之后正确地释放内存 #include <new> struct Foo { int hello; int world; }; int main() { Foo* foo = new Foo; // Do something

我想为一个对象回收内存,而不是释放和重建它。假设
Foo
实际上不包含指针(但可能包含函数),下面使用“placement new”是否安全

另外,最后的
delete
调用是否安全,它是否会正确地调用第二个“new”对象上的析构函数,并在之后正确地释放内存

#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);
}
#包括
结构Foo{
int你好;
int世界;
};
int main(){
Foo*Foo=新的Foo;
//和福做点什么
//使用foo完成,在旧版本的基础上编写新版本的foo。
新(foo)foo();
删除(foo);
}

上面的简单示例编译并运行时没有错误,但我无法通过运行它来判断它是否会在更复杂的环境中因某种原因而崩溃。

这是安全的,因为您要覆盖的对象有一个微不足道的析构函数。根据n3337第3.8章(对象寿命):

4程序可以通过重用对象占用的存储或通过显式方式结束任何对象的生命周期 使用非平凡析构函数为类类型的对象调用析构函数。为类类型的对象调用析构函数 对于非平凡析构函数,程序不需要在存储之前显式调用析构函数 对象占用的资源被重新使用或释放;但是,如果没有显式调用析构函数,或者 删除表达式(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)
让我们回顾一下,从任何对象开始都可以:

最后,使用相同的动态类型:


正如您所看到的,标准并没有说当析构函数非常重要时,它会爆炸。只有当你的程序依赖于析构函数的副作用时,你才能得到UB,但即使是UB也不一定意味着爆炸。@KonstantinOznobihin是的,它可能,但不是必须的。改变了这一点。很多人都依赖于析构函数。。对于纯数据容器(可能是构建到位最有用的东西,因为可以避免许多小的分配),您可以安全地这样做。C++11解决方案是将新对象移到旧对象之上。既安全又高效。为什么要跳过析构函数?@PeteBecker,如果没有必要,我不想在用新构造函数覆盖内存时为函数调用支付任何性能成本。@merlin2011-如果没有必要(即,它是一个微不足道的析构函数),编译器将跳过它。如果析构函数做了一些有意义的事情,这是必要的。”…如果析构函数有副作用(见§3.8/4),“只有当程序“依赖”副作用时,否则会导致未定义的行为。例如,你的第二个例子定义了行为。@Casey:如果你说的是
Bar
,我认为内存泄漏是未定义行为的表现;否则,请精确说明哪个示例…我指的是
Bar
,是的。我声称它定义了行为,就像
void foo(){new int;}
所定义的那样;泄漏内存定义了行为。@凯西:我可以看到你的观点,我想,但是我建议你认为<代码> Bar <代码>是不是拿着一个锁?或者拥有一个文件描述符?一般来说,程序的正确性是基于获得的资源的适当释放(以免造成OOM情况、死锁等);在我看来,只有当程序“依赖”副作用时,这个表达才是故意含糊的,以解决所有可能的问题。是的,这都是真的-它说明了情况比您描述的要复杂一些,因为您声称函数
UNDEFINED_BEHAVIOR
必然有未定义的行为。这里的标准很明确:泄漏的资源不一定会导致未定义的行为,除非泄漏了足够的资源,从而使程序受到影响,从而导致资源匮乏。你可以说这是一种可怕的风格,作者应该被鞭笞,我整天都同意你的观点——但你不能说标准禁止这样做。既然新的正在发生,我们不需要再次调用新对象的析构函数吗?
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...
}