Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Call destructor and then constructor (resetting an object)

People also ask

Can you call a destructor in a constructor?

When you throw from the constructor, it will call the destructor of any object constructed so far: the member variables and the inherited classes (section 15.2/2). If you call the destructor manually, their destructor will be also called (section 12.4/8).

How do you clear a class object in C++?

In C++, the single object of the class which is created at runtime using a new operator is deleted by using the delete operator, while the array of objects is deleted using the delete[] operator so that it cannot lead to a memory leak.

How does constructor and destructor work?

Constructors are special class functions which performs initialization of every object. The Compiler calls the Constructor whenever an object is created. Constructors initialize values to object members after storage is allocated to the object. Whereas, Destructor on the other hand is used to destroy the class object.

What happens when the destructor is called?

A destructor is a member function that is invoked automatically when the object goes out of scope or is explicitly destroyed by a call to delete . A destructor has the same name as the class, preceded by a tilde ( ~ ).


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.


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.


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.


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.


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.


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