Call destructor and then constructor (resetting an

2020-01-31 04:29发布

I want to reset an object. Can I do it in the following way?

anObject->~AnObject();
anObject = new(anObject) AnObject();
// edit: this is not allowed: anObject->AnObject();

This code is obviously a subset of typical life cycle of an object allocated by in 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

The only thing that's changed is the order of constructor and destructor calls.

So, why in the following FAQ all the threatening appear?

[11.9] But can I explicitly call a destructor if I've allocated my object with new?

FAQ: You can't, unless the object was allocated with placement new. Objects created by new must be deleted, which does two things (remember them): calls the destructor, then frees the memory.

FQA: Translation: delete is a way to explictly call a destructor, but it also deallocates the memory. You can also call a destructor without deallocating the memory. It's ugly and useless in most cases, but you can do that.

The destructor/constructor call is obviously normal C++ code. Guarantees used in the code directly result from the in placement new guarantees. It is the core of the standard, it's rock solid thing. How can it be called "dirty" and be presented as something unreliable?

Do you think it's possible, that the in-placement and non-in-placement implementation of new are different? I'm thinking about some sick possibility, that the regular new can for example put size of the memory block allocated before the block, which in-placement new obviously would not do (because it doesn't allocate any memory). This could result in a gap for some problems... Is such new() implementation possible?

10条回答
Animai°情兽
2楼-- · 2020-01-31 04:48

You cannot call the constructor in the manner indicated by you. Instead, you can do so using placement-new (like your code also indicates):

new (anObject) AnObject();

This code is guaranteed to be well-defined if the memory location is still available – as it should be in your case.

(I've deleted the part about whether this is debatable code or not – it's well-defined. Full stop.)

By the way, Brock is right: how the implementation of delete isn't fixed – it is not the same as calling the destructor, followed by free. Always pair calls of new and delete, never mix one with the other: that's undefined.

查看更多
Luminary・发光体
3楼-- · 2020-01-31 04:49

If your object has sensible assignment semantics (and correct operator=), then *anObject = AnObject() makes more sense, and is easier to understand.

查看更多
Root(大扎)
4楼-- · 2020-01-31 04:51

Note that they're not malloc and free that are used, but operator new and operator delete. Also, unlike your code, by using new you're guaranteeing exception safety. The nearly equivalent code would be the following.

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

anObject->~AnObject();
::operator delete(anObject)

The reset you're proposing is valid, but not idiomatic. It's difficult to get right and as such is generally frowned upon and discouraged.

查看更多
小情绪 Triste *
5楼-- · 2020-01-31 05:00

Why not implement a Clear() method, that does whatever the code in the body of the destructor does? The destructor then just calls Clear() and you call Clear() directly on an object to "reset it".

Another option, assuming your class supports assignment correctly:

MyClass a;
...
a = MyClass();

I use this pattern for resetting std::stack instances, as the stack adaptor does not provide a clear function.

查看更多
登录 后发表回答