Why is my struct destructor not called?

Started by
17 comments, last by King Mir 8 years ago

Hey all check this out:

struct A
{
    int val = 0;

    A() { printf( "Construct A [%d]", val ); }
    ~A() { printf( "Destruct A [%d]", val ); }
};

int main()
{
    A a;
    a.val = 500;

    a = A();
    printf( "Finally %d", a.val );
    a.val = 300;
    return 0;
}

Produces output:

Construct A [0]
Construct A [0]
Destruct A [0]
Finally 0
Destruct A [300]


What the!? Why am I not getting Destruct A [500]?! Memory leaks if I would have some pointers in A cuz its destructor is never called!? I want to see something like this:


Construct A [0]
[color=red]Destruct A [500]
Construct A [0]
Destruct A [0]
Finally 0
Destruct A [300]


What am I doing wrong? Hellup
Advertisement

You have two instances of A. The variable named a, which has a "val" of 300 at the time it is destructed, and the temporary created on the line "a = A();".

To be clear, that quoted line creates a temporary A, copies its value into the variable named "a" via the assignment operator, and then destructs the temporary.

Be sure you're not testing with optimizations on, the temporary is probably elided by the compiler in this case.

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]


What the!? Why am I not getting Destruct A [500]?! Memory leaks if I would have some pointers in A cuz its destructor is never called!? I want to see something like this:


Construct A [0]
Destruct A [500]
Construct A [0]
Destruct A [0]
Finally 0
Destruct A [300]

That wouldn't make sense, because then your constructors and destructors aren't matched.

The line


a = A()

doesn't call the destructor on a (I'm guessing that's what you're expecting based on your desired output). It simply calls a's assignment operator (and the temporary's constructor and destructor, unless elided as ApochPiQ said).

Ah right it's the assignment operator that gets called. Weird, for some reason I expected the old a to be destructed. Must be because it's late. Thanks.

So you don't need to worry about memory leaks from this - the new A is destroyed, the variable a is only changed by assignment.
Destructors are only called when a variable goes out of scope (RAII), a temporary is created and destroyed (ie via a = A(); ), or when objects are explicitly destroyed (via delete, for example).

Why would it destroy the object twice?

You stomp over the '500' instance with the a = A(); call.

You are dabbling in an area that I think is unique to C++.

Other languages like Java, C#, et. al. don't (easily or at-all) allow you to construct an object on the stack.

C++ won't garbage collect either so if you did this with (heap) pointers you would leak the '500' instance.

You could explicitly invoke the dtor before you invoke the ctor again.

a.~A();

a = A();

- The trade-off between price and quality does not exist in Japan. Rather, the idea that high quality brings on cost reduction is widely accepted.-- Tajima & Matsubara

You could explicitly invoke the dtor before you invoke the ctor again.
a.~A();
a = A();

This would be a bad idea in the general case. The assignment operator would assume that the object is in a valid state, but the destructor for most non trivial objects would put it in an invalid state. Ex: it may deallocate internal buffers, but the assignment operator may assume that those buffers are allocated.

There are ways to make the pattern work, but you must know far more about memory management than you currently do.

The pattern is to use placement new on the memory address to create a new object at the location without automatically invoking the destructor. There are several risks involved, especially if it is using a polymorphic type.

The pattern is to destroy the object by explicitly calling the destructor, then use placement new to construct an object at that location in memory without regard to its prior contents.

It isn't really that difficult to do in code, I typed in the few characters involved but then removed them because if it is a pattern you are going to use you should invest the time to study out the side effects and potentially code-fatal consequences involved.

It is a fairly advanced technique in the language that comes with a lot of responsibility by assuming you know all the side effects and have taken steps to address them.

For completeness sake, in C++11 there is also std::is_trivially_destructible which officially allows you to copy-construct via placement new repeatedly without bothering to call the destructor.

This topic is closed to new replies.

Advertisement