Reputation: 5856
Say I have a fixed memory buffer
char *buffer;
And I allocate my structures in that buffer using placement 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'm supposed to manually call the destructor of such allocated items but if there's no bookeeping / resource management involved is it ok to omit this? In other words if the destructor of the classes using the buffer is not doing anything (similar to ~S()
above) is it ok to skip this step? If that's the case can I reuse the buffer without destroying the previous tenants?
Upvotes: 10
Views: 1238
Reputation: 1379
Technically the destructor call is not required. Practically better safe than sorry (do call the destructor)
Upvotes: 5
Reputation: 12047
In addition to Ben Voigt's answer which details when it is OK to omit the destructor call, it is important to ensure the memory is properly aligned for the type to be placed-new in it. I'll try to write it up here as requested by to OP.
This line:
S *newS = new(buffer + offset)S;
works only if the address buffer + offset
is aligned properly:
3.11 Alignment
1 Object types have alignment requirements (3.9.1, 3.9.2) which place restrictions on the addresses at which an object of that type may be allocated. An alignment is an implementation-defined integer value representing the number of bytes between successive addresses at which a given object can be allocated.
[...]
buffer
itself is properly aligned for any type with fundamental alignment requirement:
3.7.4.1 Allocation functions
2 [...]
The pointer returned shall be suitably aligned so that it can be converted to a pointer of any complete object type with a fundamental alignment requirement (3.11) and then used to access the object or array in the storage allocated
[...]
To know the alignment requirement of a type, there is alignof(type)
. Then there is std::max_align_t
, alignof(std::max_align_t)
returns the greatest alignment value of all types with fundamental alignment requirement.
There is a special case of types that require an extended alignment, to be sure your type is not one of these I would include this in your program:
static_assert(alignof(S) <= alignof(std::max_align_t),
"Extended alignment required for S");
Then, you just have to make sure that offset
is a multiple of alignof(S)
.
Upvotes: 4
Reputation: 283634
The standard has a rule in section 3.8 [basic.life]
that covers this:
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. 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; however, if there is no explicit call to the destructor or if a delete-expression (5.3.5) is not used to release the storage, the destructor shall not be implicitly called and any program that depends on the side effects produced by the destructor has undefined behavior.
Lots of experts are in agreement that "depends on the side effects produced by the destructor" is far too vague to be useful. Many interpret it as a tautology meaning "If the program has undefined behavior when the destructor side effects are not evaluated, then failing to call the destructor causes undefined behavior". See Observable behavior and undefined behavior -- What happens if I don't call a destructor?
If your type has a trivial destructor (which appears to be the case in your example), then calling it (or failing to call it) has no effect whatsoever -- calling a trivial destructor does not even end the life of the object.
The lifetime of an object
o
of typeT
ends when:
- if
T
is a class type with a non-trivial destructor, the destructor call starts, or- the storage which the object occupies is released, or is reused by an object that is not nested within
o
.
That is, if T
doesn't have a non-trivial destructor, the only way to end the lifetime of object o
is to release or reuse its storage.
Upvotes: 18