RAII and Deterministic Destruction

cym13 via Digitalmars-d-learn digitalmars-d-learn at puremagic.com
Tue Aug 25 18:29:28 PDT 2015


On Tuesday, 25 August 2015 at 22:35:57 UTC, Jim Hewes wrote:
> Although C++ can be ugly, one reason I keep going back to it 
> rather then commit more time to reference-based languages like 
> C# is because I like deterministic destruction so much. My 
> question is whether D can REALLY handle this or not. I've not 
> been sure about this for some time so now I'm just going to 
> come out and finally ask.
>
> I know about this RAII section in the documentation: 
> http://dlang.org/cpptod.html#raii
> But I don't believe that handles all cases, such as having 
> classes as member variables of other classes. (Do the members 
> get destructors called too?)
>
> Then there is std.typecons.Unique and  std.typecons.RefCounted. 
> With these, can I really get deterministic destruction for all 
> cases like I would in C++?
>
> If so, it might be a good idea to emphasize this more in the 
> documentation because I'd think people coming from C++ would be 
> looking for this.
>
> Jim

Hi Jim. RAII in D is a common issue for people coming from C++. I 
don't know everything on the subject and would be glad to be 
corrected if I write a mistake but here is my share on the 
subject.

There are two very different kind of objects in D and memory 
management is different for each: structs and classes. I think 
you mostly want to hear about classes, but I think each should be 
studied nonetheless.

Structs:
=====

Why would you use structs?

- They are stack-allocated by default (but can be heap-allocated 
at will)
- They are destroyed at the end of the scope, hence supporting 
RAII properly
- They provide rather nice idioms for RAII [1]
- They support single-inheritance through “alias this”
- They support compile-time polymorphism through templates
- They support complex object composition through template mixins 
[2]

Why wouldn't you use structs?

- You want to plug yourself on an already existing class-based 
system
- You want to use interfaces
- You need runtime-polymorphism

Classes:
=====

Why would you use classes?

- They are heap-based and managed by the GC: lazy destruction
- They provide full support for inheritance and OOP (including 
interfaces)
- They support compile-time polymorphism through templates
- They support complex object composition through template mixins 
[2]

Why wouldn't use classes?

- They are heap-based
- They don't allow deterministic implicit destruction

How to solve these problems?
==================

Classes don't have *implicit* deterministic destruction but 
nothing prevents you from calling a destructor explicitely (be it 
~this or another dedicated function). This can be easily done 
using scope(...) statements. If this is cumbersome, 
std.typecons.Unique and std.typecons.RefCounted can be used to 
provide a comportment analogous to that of modern C++. We said 
that structs are scope-based, a solution is to wrap an object 
into a struct and set the structs destructor to destroy the 
object as well at the end of the scope. std.typecons.scoped 
provides a nice wrapper arround this behaviour. The destruction 
of an object doesn't mean the destruction of the objects that it 
referenced. That can also be done explicitely.

A word on the GC
===========

I see a lot of C++ programmer who come to D with a single thought 
in mind: “Avoid the GC at all cost, let me manage my damn 
memory”. This is understandable, yet biaised. True, a GC isn't 
for all applications, but it has its place. Instead of fearing 
it, learning to use it well is important. One often doesn't need 
deterministic destruction. Lazy destruction has its benefits. I 
strongly advise not to rush trying to avoid the GC, instead 
profile profile profile.

Conclusion
=======

Doing RAII is possible for structs and for classes, but it is 
*really* easier with structs. D structs are really powerful and 
often underestimated by newcommers who rush on the "class" 
keyword. D classes aren't C++ classes. My advice would be to use 
structs as much as possible, to use classes where needed relying 
on the GC, and after profiling to introduce RAII behaviour into 
problematic classes.

[1] 
https://w0rp.com/blog/post/an-raii-constructor-by-another-name-is-just-as-sweet/
[2] 
https://blog.dicebot.lv/posts/2015/08/OOP_composition_with_mixins


More information about the Digitalmars-d-learn mailing list