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.
小智 20
为什么不实现一个Clear()方法,它可以解析析构函数体内的代码呢?析构函数然后只调用Clear()并直接在对象上调用Clear()来"重置"它.
另一个选项,假设您的类正确支持赋值:
MyClass a;
...
a = MyClass();
Run Code Online (Sandbox Code Playgroud)
我使用此模式重置std :: stack实例,因为堆栈适配器不提供清除功能.
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()方法以清除类中所有对象的值.
您无法以您指定的方式调用构造函数.相反,您可以使用placement-new(如您的代码也指示)来执行此操作:
new (anObject) AnObject();
Run Code Online (Sandbox Code Playgroud)
如果内存位置仍然可用,则保证此代码定义良好 - 就像在您的情况下一样.
(我已经删除了关于这是否是有争议的代码的部分 - 它是明确定义的.完全停止.)
顺便说,布洛克是正确的:如何执行delete是不固定的-这是不一样的调用析构函数,其次是free.始终配对的电话new和delete,从来没有一个混合与其他:这是不确定的.
请注意,他们不是malloc和free被使用,但operator new和operator 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)
您提出的重置是有效的,但不是惯用的.很难做到正确,因此通常不赞成和灰心.
是的,您所做的大部分时间都是有效的。[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)