In the code that follows, the object sub in class C is constructed twice. The first construction calls the default ctor Sub() and the second construction uses placement new to reconstruct this object in the same address.

Therefore the destructors are also called twice. The first call uses the direct call to the Sub dtor in ~C() and the second call is invoked after the end of main(), I believe, by the atexit() function.

Given that the object sub is reconstructed at the same address, how does the compiler knows that the second destructor must be called after main()? Where does he keep this information?

#include <iostream>
using namespace std;

struct Table
{
    int i;
    Table(int j) : i(j) {}
};

struct Sub
{
    Table* pTable;
    Sub(int j) { cout << "ctor placement new" << endl; pTable = new Table(j); }
    Sub() { cout << "ctor default" << endl; pTable = 0; }
    ~Sub() { if( pTable ) cout << "dtor placement new" << endl;
             else         cout << "dtor default" << endl;
             delete pTable; pTable = 0; }
};

class C
{
    Sub sub;

    public:
    C() { new (&sub) Sub(10); }
    ~C() { (&sub)->~Sub(); }
};

int main()
{
    C c;
}
4

There are 4 best solutions below

10
On BEST ANSWER

Although this is clearly undefined behavior, if you reason out what's happening, it's pretty obvious.

You create an object of class C. In that process, the default constructor of Sub is called implicitly. pTable is 0. Then, you explicitly call the int constructor, which initializes pTable. Then, in the destructor, you explicitly call Sub's destructor. pTable is set to 0 again. Then, at the end of C's destructor, Sub's destructor is called again, implicitly.

It's not at the end of main that it's happening. It's happening at the end of C's destructor.

6
On

Your assumption about atexit() is incorrect. The destructor for sub is called by the destructor for C when the object c goes out of scope in main().

A C++ destructor always calls destructors for all its sub-objects.

Your code is invalid anyway, because you're calling the placement new operator on a chunk of memory (sub) that has already been constructed into an object. Similarly to the destructor, a C++ constructor always calls constructors for all its sub-objects.

2
On

When c goes out of scope, its destructor is called. The C destructor will explicitly call the sub destructor. When the C destructor is done, the sub destructor will also be called (again), because all C++ destructors automatically call the destructors of all their internal objects.

Essentially, the code

(&sub)->~Sub();

is unnecessary, and incorrect. You should never explicitly call the destructor of a managed object.

Edit: It's valid to explicitly call the destructor on an object that was constructed via placement new. However this is only the case when the object is not managed. For example:

class C
{
    Sub sub[1];

    public:
    C() { new (sub) Sub(10); }
    ~C() { sub->~Sub(); }
};

This is not only valid, but necessary because the member of C is of type Sub[1] (or more generally Sub*), so Sub's destructor will not be called explicitly when C is destroyed.

0
On

The other answers make valid points about undefined behavior, but I'm surprised no one's mentioned how this program should be corrected.

Sub's destructor does need to be called manually here, but it needs to be called before the placement new, not during destruction:

class C
{
    Sub sub;

    public:
    C() { 
        (&sub)->~Sub();
        new (&sub) Sub(10); 
    }
};

In the current code you create an instance of C, which default constructs an instance of Sub then immediately placement news another instance of Sub on top of the old one without calling its destructor. At the end of main c's destructor gets called, which calls sub's destructor explicitly, then sub's destructor gets called again on the already destructed object. Not only is this undefined behavior, if Sub's default constructor allocated you would also have a memory leak.

In the corrected code sub is default constructed, destructed manually, constructed via placement new, then destructed implicitly when c falls out of scope at the end of main.

So the answer is that the compiler doesn't know that the destructor needs to be called when you recreate the object at the same address because placement new doesn't take an object as its first parameter, it takes a void*. There's no way for it to determine whether that pointer is pointing to an existing object, so you have to call the destructor manually.