Managing malloced memory

Mike Parker aldacron at
Mon Oct 11 14:03:28 UTC 2021

On Monday, 11 October 2021 at 10:53:15 UTC, anon wrote:

>> S makeS(int x)
>> {
>>    return S(x); // no destructor called here.
>> }
>> void main()
>> {
>>    foo(S(1)); // no destructor called for this rvalue
>>    auto s = makeS(1);
>>    // destructor for s called here.
>>    foo(makeS(1)); // only one destructor called at the end of 
>> foo
>> }
>> ```
> Is there any reference for exactly how these rules apply, or is 
> this implementation defined? The 
> [specification]( says that destructors are called when objects go out of scope. Your examples seem to suggest that this is untrue in some cases.

For example, in `makeS` the initializer combined with the return 
triggers an optimization (return value optimization, or RVO)) 
that elides a copy of the struct, meaning there's nothing to 
destroy at the end of `makeS`. The destruction will occur in the 
scope into which the instance is moved.

Any time you have a named instance, like `S s = S(1)`, you can 
pretty much guarantee its destructor will be called. An exception 
is when `s` is returned immediately after the initialization, 
then NRVO (named return value optimization) can kick in to elide 
the copy and, therefore, the destruction again happens at the end 
of the scope into which the instance is moved.

Play around with a struct destructor that prints a message and 
you'll get a feel for when destructors are and aren't called. 
Like Steve said, it's once per copy. Sometimes you end up with 
temporaries that are destroyed, sometimes you don't depending on 
compiler optimizations.

More information about the Digitalmars-d-learn mailing list