Does the object created with the new placement have dynamic storage capacity?

(5.3.4)

new expression:

  • :: opt _ new new-placement_ opt new-type-id new-initializeropt

  • :: opt _ new new-placement_ opt (type-id) new-initializeropt

Objects created by the new expression have a dynamic storage duration (3.7.4). [Note: the lifetime of such an object is not necessarily limited to the area in which it is created . - final 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 @MM claims that there is only one object (local_object), and the rest are just pointers. Is it correct?

(3.7)

Dynamic storage duration associated with objects created using the new operator

+7
c ++ language-lawyer
source share
1 answer

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

This exact scenario is foreseen by the standard in clause 8 [3.8] [basic.life] with reference to the following undefined behavior example:

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

The paragraph says:

If the program ends the lifetime of an object of type T with static (3.7.1), streaming (3.7.2), or automatic (3.7.3) storage time and if T has a nontrivial destructor, the program must ensure that the object of the original type takes up the same place storage when an implicit call to the destructor takes place; otherwise, the behavior of the program is undefined.

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

A program can end the lifetime of any object by reusing the storage that the object occupies, or by explicitly calling the destructor for an object of the class type with a nontrivial destructor.

In this example, the b destructor code was not called, but this is acceptable, since in paragraph 4 it is explicitly allowed to call a nontrivial destructor:

For an object of class type with a nontrivial destructor, the program does not require a direct call to the destructor before the storage that the object occupies is reused or freed;

while the program is ready to live with the consequences of a non-called destructor.

But to return to point 8, the lifetime b ended, and the repository was reused to create an object of type T This object has a dynamic storage duration, which means that its destructor will not be called implicitly. As above, it is not necessary to explicitly call the destructor if the program does not require any side effects that may be performed by the destructor.

Although b ended, the fact that b had an automatic storage duration means that its destructor will be implicitly called when the control flow goes out of scope. Calling a destructor for an object whose lifetime has expired is a specific case of prohibiting the use of an object whose life has expired, according to clause 6 of section 3.8, which prohibits calling the function of a non-static member member of an object whose lifetime has expired but has not yet been repeatedly used or released.

For this reason, an example of program behavior is undefined.

But paragraph 7 of this section gives a mechanism that allows a program to avoid undefined behavior by re-creating another object of the same type in the same place:

If after the life of the object has expired and before the repository that the object is busy reused or released, a new object is created in the storage location where the original object was loaded, a pointer pointing to the original object, a link related to the original object, or the name of the source object will automatically refer to the new object and, as soon as the lifetime of the new object is started, it can be used to manage the new object if:

(7.1) - the storage for the new object exactly overlays the storage location that was occupied by the original object, 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 source object is not constant, and if the class type does not contain any non-static data member whose type is constant or reference, and

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

So, in my interpretation, the following snippet would define behavior:

 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 implies the possibility that an object with dynamic storage duration can be created using memory allocated for an object with automatic storage time, and provides a set of restrictions and requirements for a well-defined program that performs this action, thereby avoiding the consequences of performing an implicit a destructor at the facility whose service life was ended by reusing its storage.

+7
source share

All Articles