调用析构函数然后构造函数(重置对象)

24 c++ memory-management new-operator

我想重置一个对象.我可以通过以下方式进行吗?

anObject->~AnObject();
anObject = new(anObject) AnObject();
// edit: this is not allowed: anObject->AnObject();
Run Code Online (Sandbox Code Playgroud)

此代码显然是在placement new中分配的对象的典型生命周期的子集:

AnObject* anObject = malloc(sizeof(AnObject));
anObject = new (anObject) AnObject(); // My step 2.
// ...
anObject->~AnObject(); // My step 1.
free(anObject)
// EDIT: The fact I used malloc instead of new doesn't carry any meaning
Run Code Online (Sandbox Code Playgroud)

唯一改变的是构造函数和析构函数调用的顺序.

那么,为什么在以下常见问题解答中 出现所有威胁呢?

[11.9]但是如果我用new分配了我的对象,我可以显式调用析构函数吗?

常见问题解答:除非对象分配了新的展示位置,否则您不能.必须删除new创建的对象,它会做两件事(记住它们):调用析构函数,然后释放内存.

FQA:翻译:删除是一种明确调用析构函数的方法,但它也会释放内存.您也可以在不释放内存的情况下调用析构函数.在大多数情况下它很丑陋而且无用,但你可以做到这一点.

析构函数/构造函数调用显然是普通的C++代码.代码中使用的保证直接来自放置新保证.它是标准的核心,它坚如磐石.它怎么称为"脏"并被表现为不可靠的东西?

您是否认为新的插入式和非插入式实施是不同的?我正在考虑一些病态的可能性,例如,常规的新的可以放置在块之前分配的内存块的大小,其中放置新的显然不会这样做(因为它不分配任何内存).这可能会导致某些问题出现间隙...... 这样的新()实现是否可行?

MSa*_*ers 29

不要被FQA巨魔吸引住.像往常一样,他弄错了事实.

您当然可以直接为所有对象调用析构函数,无论它们是否使用placement new创建.丑陋是在旁观者的眼中,它确实很少需要,但唯一的事实是内存分配和对象创建必须平衡.

"Regular"new/delete通过将内存分配和对象创建结合在一起简化了这一过程,并且堆栈分配通过为您完成这两项操作进一步简化了它.

但是,以下内容完全合法:

int foo() {
    CBar bar;
    (&bar)->~CBar();
    new (&bar) CBar(42);
 }
Run Code Online (Sandbox Code Playgroud)

两个对象都被破坏,堆栈内存也会自动回收.但与FQA声明不同的是,析构函数的第一次调用之前没有放置new.

  • 为什么用((&bar)->〜CBar();`而不是`bar。〜CBar();`? (2认同)

小智 20

为什么不实现一个Clear()方法,它可以解析析构函数体内的代码呢?析构函数然后只调用Clear()并直接在对象上调用Clear()来"重置"它.

另一个选项,假设您的类正确支持赋值:

MyClass a;
...
a = MyClass();
Run Code Online (Sandbox Code Playgroud)

我使用此模式重置std :: stack实例,因为堆栈适配器不提供清除功能.

  • 你必须维护析构函数"正确性" - 我建议将析构函数代码移到Clear()方法中,因此析构函数只包含对Clear()的单个调用.我用这种方式编写几乎所有自己的代码,BTW. (3认同)
  • 因为它需要保持Clear()方法的正确性. (2认同)
  • 这取决于类的语义.如果在清除之后重建课程是昂贵的,那么请使用我的第二个建议.我要说的是,要么使用显式的析构函数调用和使用placement new,要么可能不那么复杂. (2认同)

Bro*_*olf 14

从技术上讲,明确调用构造函数或析构函数是不好的做法.

delete关键字在您使用时最终会调用它们.对于构造函数而言,同样如此.

对不起,但那段代码让我想把头发撕掉.你应该这样做:

分配对象的新实例

AnObject* anObject = new AnObject();
Run Code Online (Sandbox Code Playgroud)

删除对象的实例

delete anObject;
Run Code Online (Sandbox Code Playgroud)

永远不要这样做:

anObject->~AnObject(); // My step 1.
free(anObject)
Run Code Online (Sandbox Code Playgroud)

如果你必须"重置"一个对象,要么创建一个方法来清除里面的所有实例变量,要么我建议你做的是删除对象并为自己分配一个新对象.

"这是语言的核心?"

这没什么意义.Perl有大约六种编写for循环的方法.仅仅因为你可以用语言做事,因为它们是受支持的,这意味着你应该使用它们.哎呀我可以用switch语句编写所有代码,因为语言的"核心"支持它们.不是一个好主意.

你明白没有必要时为什么要使用malloc?Malloc是一种C方法.

New和Delete是您在C++中的朋友

"重置"一个对象

myObject.Reset();
Run Code Online (Sandbox Code Playgroud)

你去吧 这样可以避免以危险的方式不必要地分配和释放内存.编写Reset()方法以清除类中所有对象的值.

  • 看看我的答案为什么. (2认同)
  • Reset() 方法需要维护。 (2认同)

Kon*_*lph 9

您无法以您指定的方式调用构造函数.相反,您可以使用placement-new(如您的代码也指示)来执行此操作:

new (anObject) AnObject();
Run Code Online (Sandbox Code Playgroud)

如果内存位置仍然可用,则保证此代码定义良好 - 就像在您的情况下一样.

(我已经删除了关于这是否是有争议的代码的部分 - 它是明确定义的.完全停止.)

顺便说,布洛克是正确的:如何执行delete是不固定的-这是一样的调用析构函数,其次是free.始终配对的电话newdelete,从来没有一个混合与其他:这是不确定的.


ava*_*kar 7

请注意,他们不是mallocfree被使用,但operator newoperator delete.此外,与您的代码不同,通过使用新的,您可以保证异常安全.几乎相同的代码如下.

AnObject* anObject = ::operator new(sizeof(AnObject));
try
{
    anObject = new (anObject) AnObject();
}
catch (...)
{
    ::operator delete(anObject);
    throw;
}

anObject->~AnObject();
::operator delete(anObject)
Run Code Online (Sandbox Code Playgroud)

您提出的重置是有效的,但不是惯用的.很难做到正确,因此通常不赞成和灰心.


Rak*_*111 5

是的,您所做的大部分时间都是有效的。[basic.life]p8说:

如果在一个对象的生命周期结束后,在该对象所占用的存储空间被重用或释放之前,在原对象所占用的存储位置创建一个新对象,一个指向原对象的指针,一个指向该对象的引用引用原始对象,或者原始对象的名称将自动引用新对象,并且一旦新对象的生命周期开始,可用于操作新对象,如果:

  • 新对象的存储正好覆盖原始对象占用的存储位置,并且

  • 新对象与原始对象的类型相同(忽略顶级 cv 限定符),并且

  • 原始对象的类型不是 const 限定的,并且,如果是类类型,则不包含任何类型为 const 限定或引用类型的非静态数据成员,并且

  • 原始对象和新对象都不是潜在重叠的子对象([intro.object])。

因此,如果您没有const或引用成员,则是合法的。

如果你没有这个保证,std::launder如果你想使用新对象,你需要使用或使用由放置 new 返回的指针(就像你正在做的那样):

// no const/ref members
anObject->~AnObject(); // destroy object
new (anObject) AnObject(); // create new object in same storage, ok

anObject->f(); // ok

// const/ref members
anObject->~AnObject();
auto newObject = new (anObject) AnObject();

anObject->f(); // UB
newObject->f(); // ok
std::launder(anObject)->f(); // ok
Run Code Online (Sandbox Code Playgroud)