Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does an object created with placement new have dynamic storage duration?

(5.3.4)

new-expression:

  • ::opt_new new-placement_opt new-type-id new-initializeropt

  • ::opt_new new-placement_opt ( type-id ) new-initializeropt

Entities created by a new-expression have dynamic storage duration (3.7.4). [ Note: the lifetime of such an entity is not necessarily restricted to the scope in which it is created. — end note ]

I think the following has 1 main object (local_object) with automatic storage duration, and 3 dummy classes with dynamic storage duration.

struct dummy
{
    int a;
};

char local_object[256];
dummy * a = new(&local_object) dummy;
dummy * b = new(&local_object +100) dummy;
dummy * c = new(&local_object +200) dummy;

The user @M.M. argues that there's only one object (local_object), and that the rest are just pointers. Is this correct?

(3.7)

The dynamic storage duration is associated with objects created with operator new

like image 934
Jts Avatar asked Feb 14 '16 08:02

Jts


1 Answers

It seems to me that the standard (as quoted in the OP) can only be interpreted as it reads, which is that operator new creates objects of dynamic storage duration, and this is true even if the underlying memory was obtained for an object of automatic duration.

This precise scenario is anticipated by the standard in §3.8 [basic.life] paragraph 8, with reference to the following example of undefined behaviour:

class T { };
struct B {
    ~B();
};
void h() {
    B b;
    new (&b) T;
}

The paragraph reads:

If a program ends the lifetime of an object of type T with static (3.7.1), thread (3.7.2), or automatic (3.7.3) storage duration and if T has a non-trivial destructor, the program must ensure that an object of the original type occupies that same storage location when the implicit destructor call takes place; otherwise the behavior of the program is undefined.

In the example, the program has "ended the lifetime" of the object b by reusing its storage, as provided by paragraph 4 of the same section: (emphasis added).

A program may end the lifetime of any object by reusing the storage which the object occupies or by explicitly calling the destructor for an object of a class type with a non-trivial destructor.

In the example code, b's destructor was not called but this is acceptable because paragraph 4 explicitly allows a non-trivial destructor to not be called:

For an object of a class type with a non-trivial destructor, the program is not required to call the destructor explicitly before the storage which the object occupies is reused or released;

as long as the program is prepared to live with the consequences of the destructor not being called.

But to return to paragraph 8, b's lifetime has ended, and the storage has been reused to create an object of type T. This object has dynamic storage duration, which means that its destructor will not be called implicitly. As above, it is not obligatory to explicitly call the destructor either, as long as the program does not require any side-effects which might be performed by the destructor.

Despite the fact that the lifetime of b has ended, the fact that b had automatic storage duration means that its destructor will be implicitly called when control flow leaves its scope. Calling a destructor on an object whose lifetime has ended is a specific case of the prohibition on using an object whose lifetime has ended, as per paragraph 6 of §3.8, which prohibits calling a non-static member member function of an object whose lifetime has ended but whose storage has not yet been reused or released.

For this reason, the example program's behaviour is undefined.

But paragraph 7 of this section provides a mechanism for the program to avoid the undefined behaviour by recreating a different object of the same type at the same location:

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:

(7.1) — the storage for the new object exactly overlays the storage location which the original object occupied, and

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

(7.3) — 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

(7.4) — the original object was a most derived object (1.8) of type T and the new object is a most derived object of type T (that is, they are not base class subobjects).

So, in my interpretation, the following snippet would have defined behaviour:

class T { };
struct B {
    ~B();
};
void h() {
    B b;
    new (&b) T;
    new (&b) B; /* recreate a B so that it can be destructed */
}

In short, the standard contemplates the possibility that an object of dynamic storage duration can be created using memory allocated to an object of automatic storage duration, and provides a set of restrictions and requirements for a well-defined program which performs this action, thereby avoiding the consequences of executing an implicit destructor on an object whose lifetime has been ended by reusing its storage.

like image 191
rici Avatar answered Oct 16 '22 16:10

rici