A common technique to manage resources is reference counting. D's structs provide the necessary infrastructure to automate reference counting.
In order to make a reference-counted object, perform the following steps:
struct
to hold the data, or it may be a pointer to a library.alias this
to the pointer so that operations will be automatically forwarded to it.struct RefCountedObject { private struct Implementation { /* other contents here */ int refcount; } Implementation* data; alias data this; static RefCountedObject create() { RefCountedObject o = void; o.data = new Implementation(); o.data.refcount = 1; writeln("Created."); // so we can see it in action return o; } this(this) { if(data is null) return; data.refcount++; writeln("Copied. Refcount = ", data.refcount); } ~this() { if(data is null) return; data.refcount--; writeln("Released. Refcount = ", data.refcount); if(data.refcount == 0) writeln("Destroyed."); } }
The general idea of reference counting is to increment the count every time the reference is copied and decrement it every time a reference ceases to exist. D's structs provide two functions that can do exactly this: postblits and destructors.
When a struct
object is copied, the data is first blitted (bit-block transfer; a straightforward copy of the object's memory) to the new location (a shallow copy operation). Then the postblit function, if present, is called on the recipient object. The postblit is defined with the syntax this(this) { /* code */ }
.
Whenever struct
provably ceases to exist—goes out of scope after being left behind by a thrown exception, being written over, or being collected by the garbage collector—its destructor is called, if present. The destructor is defined with the syntax ~this() { /* code */ }
.
It is important that the reference count is stored separately from struct
. If the count were a regular member variable, it would be copied along with the reference, making it useless. A static member variable would be shared across all instances of the struct, which makes it unsuitable to manage distinct objects as well. Instead, we use a pointer to the object with the reference count.
Since our key member is a pointer, we need to remember that it may be null and offer a way to initialize it. Since D's structs cannot have default constructors, we instead use a factory function. The factory function acquires the function and initializes the reference count to one.
Afterward, we can use the object normally, thanks to alias this
. All method calls will be forwarded to the inner object. The alias this
declaration also allows implicit conversion to the inner object's type, which could break the reference counting encapsulation. This is why we made Implementation
private. If it is not possible to make the inner object private, avoid this situation by either writing forwarding functions yourself (this can be automated with code generation, which we'll cover later in the book) or implement a code review policy, prohibiting the direct declaration of the inner object's type.
3.143.254.151