Is it possible to name a destructor when placing new selected objects?

Say I have a fixed memory buffer

char *buffer; 

And I highlight my structures in this buffer by placing new

 struct S { std::tuple<int, double, char> m_data; auto getRecord() { return m_data; } }; S *newS = new(buffer + offset)S; 

I know that I have to manually call the destructor of such selected elements, but if there is no accounting / resource management , can this be omitted? In other words, if a class destructor using a buffer does nothing (similar to ~S() above), can this be skipped? If in this case I can reuse the buffer without destroying previous tenants?

+7
source share
3 answers

The standard has a rule in section 3.8 [basic.life] , which covers this:

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. For a class type object 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 released ; however, if there is no explicit call to the destructor or if the delete expression (5.3.5) is not used to free the storage, the destructor should not be implicitly called and any program that depends on the side effects received by the destructor has undefined behavior .

Many experts agree that “depends on the side effects created by the destructor” is too vague to be useful. Many interpret it as a tautological meaning: "If the program has undefined behavior, when the side effects of the destructor are not evaluated, then refusing to call the destructor causes undefined behavior." See Observed Behavior and Undefined Behavior. What happens if I do not call the destructor?

If your type has a trivial destructor (which seems to be the case in your example), then calling it (or not calling it) has no effect - calling the trivial destructor does not even end the life of the object.

The lifetime of an object o type T ends when:

  • If T is a class type with a non-trivial destructor, then the call to the destructor begins or
  • the storage that an object occupies is freed or reused by an object that is not nested in o .

That is, if T does not have a nontrivial destructor, the only way to end the life of an object o is to free or reuse its storage.

+14
source

Technically, a call to the destructor is not required. Almost better than sorry (call the destructor)

+5
source

In addition to Ben Voigt, answer what details, when it's normal, to omit the call to the destructor, it is important to ensure that the memory alignment is correct for the type to be placed - new in it. I will try to write it here as requested by OP .

This line:

 S *newS = new(buffer + offset)S; 

only works if the buffer + offset address is correctly aligned:

3.11 Alignment
1 Types of objects have alignment requirements (3.9.1, 3.9.2), which establish restrictions on the addresses at which an object of this type can be selected. Alignment is an integer value defined by the implementation that represents the number of bytes between consecutive addresses where this object can be allocated.
[...]

buffer itself is correctly aligned for any type with a fundamental alignment requirement:

3.7.4.1 Distribution Functions
2 [...]
The returned pointer must be properly aligned so that it can be converted to a pointer to any complete object type with a fundamental alignment requirement (3.11) and then used to access the object or array in a dedicated storage [...]

To find out the alignment requirement for a type, there is alignof(type) . Then there is std::max_align_t , alignof(std::max_align_t) returns the highest alignment value of all types with a fundamental alignment requirement.

There is a special case of types that require extended alignment so that your type is not one of them; I would include it in your program:

 static_assert(alignof(S) <= alignof(std::max_align_t), "Extended alignment required for S"); 

Then you need to make sure that offset multiple of alignof(S) .

+4
source

Source: https://habr.com/ru/post/1013612/


All Articles