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条回答
等我变得足够好
2楼-- · 2020-01-31 04:41

Technically it is bad practice to call constructors or destructors explicitly.

The delete keyword ends up calling them when you use it. Same goes for new with constructors.

I'm sorry but that code makes me want to tear my hair out. You should be doing it like this:

Allocate a new instance of an object

AnObject* anObject = new AnObject();

Delete an instance of an object

delete anObject;

NEVER do this:

anObject->~AnObject(); // My step 1.
free(anObject)

If you must "reset" an object, either create a method which clears all the instance variables inside, or what I would recommend you do is Delete the object and allocate yourself a new one.

"It is the core of the language?"

That means nothing. Perl has about six ways to write a for loop. Just because you CAN do things in a language because they are supported does mean you should use them. Heck I could write all my code using for switch statements because the "Core" of the language supports them. Doesn't make it a good idea.

Why are you using malloc when you clearly don't have to. Malloc is a C method.

New and Delete are your friends in C++

"Resetting" an Object

myObject.Reset();

There you go. This way saves you from needlessly allocating and deallocating memory in a dangerous fashion. Write your Reset() method to clear the value of all objects inside your class.

查看更多
家丑人穷心不美
3楼-- · 2020-01-31 04:42

Yes, what you are doing is valid most of the time. [basic.life]p8 says:

If, after the lifetime of an object has ended and before the storage which the object occupied is reused or released, a new object is created at the storage location which the original object occupied, a pointer that pointed to the original object, a reference that referred to the original object, or the name of the original object will automatically refer to the new object and, once the lifetime of the new object has started, can be used to manipulate the new object, if:

  • the storage for the new object exactly overlays the storage location which the original object occupied, and

  • the new object is of the same type as the original object (ignoring the top-level cv-qualifiers), and

  • the type of the original object is not const-qualified, and, if a class type, does not contain any non-static data member whose type is const-qualified or a reference type, and

  • neither the original object nor the new object is a potentially-overlapping subobject ([intro.object]).

So it is legal if you don't have a const or reference member.

If you don't have this guarantee, you need to use std::launder or use the pointer returned by placement new (like you're doing anyways) if you want to use the new object:

// 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
查看更多
叛逆
4楼-- · 2020-01-31 04:44

You can't call the constructor like that, but there's nothing wrong with reusing the memory and calling placement new, as long you don't delete or free (deallocate) the memory. I must say reseting an object like this is a little sketchy. I would write an object to be explicitly resetable, or write a swap method and use that to reset it.

E.g.

anObject.swap( AnObject() ); // swap with "clean" object
查看更多
劫难
5楼-- · 2020-01-31 04:46

Why not reset using the operator=()? This is not debatable and by far more readable.

A a;
//do something that changes the state of a
a = A(); // reset the thing
查看更多
兄弟一词,经得起流年.
6楼-- · 2020-01-31 04:47

Don't get sucked in by the FQA troll. As usual he gets the facts wrong.

You can certainly call the destructor directly, for all objects whether they are created with placement new or not. Ugly is in the eye of the beholder, it is indeed rarely needed, but the only hard fact is that both memory allocation and object creation must be balanced.

"Regular" new/delete simplifies this a bit by tying memory allocation and object creation together, and stack allocation simplifies it even further by doing both for you.

However, the following is perfectly legal:

int foo() {
    CBar bar;
    (&bar)->~CBar();
    new (&bar) CBar(42);
 }

Both objects are destroyed, and the stack memory is automatically recycled too. yet unlike the FQA claims, the first call of the destructor is not preceded by placement new.

查看更多
我欲成王,谁敢阻挡
7楼-- · 2020-01-31 04:47

It is far better just to add something like a Reset() method to your object rather than play with placement new's.

You are exploiting the placement new feature which is intended to allow you to control where an object is allocated. This is typically only an issue if your hardware has "special" memory like a flash chip. IF you want to put some objects in the flash chip, you can use this technique. The reason that it allows you to explicitly call the destructor is that YOU are now in control of the memory, so the C++ compiler doesn't know how to do the deallocation part of the delete.

It is not saving you much code either, with a reset method, you will have to set the members to their starting values. malloc() doesn't do that so you are going to have to write that code in the constructor anyway. Just make a function that sets your members to the starting values, call it Reset() call it from the constructor and also from anywhere else you need.

查看更多
登录 后发表回答