Programming in D by Ali Çehreli

Programming in D by Ali Çehreli

Author:Ali Çehreli [Çehreli, Ali]
Language: eng
Format: epub, pdf
Tags: d, D programming language, dlang, tutorial
Publisher: Ali Çehreli
Published: 0101-01-01T00:00:00+00:00


scoped() to call the destructor automatically

The program above has a weakness: The scopes may be exited before the destroy() lines are executed, commonly by thrown exceptions. If the destroy() lines must be executed even when exceptions are thrown, a solution is to take advantage of scope() and other features that we have seen in the Exceptions chapter.

Another solution is to construct class objects by std.typecons.scoped instead of by the new keyword. scoped() wraps the class object inside a struct and the destructor of that struct object destroys the class object when itself goes out of scope.

The effect of scoped() is to make class objects behave similar to struct objects regarding lifetimes.

With the following changes, the program produces the expected output as before:

import std.typecons; // ... void main() { const courses = scoped!XmlElement("courses", 0); foreach (courseId; 0 .. 2) { const courseTag = "course" ~ to!string(courseId); const courseElement = scoped!XmlElement(courseTag, 1); foreach (i; 0 .. 3) { const gradeElement = scoped!XmlElement("grade", 2); const randomGrade = uniform(50, 101); writeln(indentationString(3), randomGrade); } } }

Note that there are no destroy() lines anymore.

scoped() is a function that returns a special struct object encapsulating the actual class object. The returned object acts as a proxy to the encapsulated one. (In fact, the type of courses above is Scoped, not XmlElement.)

When the destructor of the struct object is called automatically as its lifetime ends, it calls destroy() on the class object that it encapsulates. (This is an application of the Resource Acquisition Is Initialization (RAII) idiom. scoped() achieves this by the help of templates and alias this, both of which we will see in later chapters.)

It is desirable for a proxy object to be used as conveniently as possible. In fact, the object that scoped() returns can be used exactly like the actual class type. For example, the member functions of the actual type can be called on it:

import std.typecons; class C { void foo() { } } void main() { auto p = scoped!C(); p.foo(); // Proxy object p is being used as type C }

However, that convenience comes with a price: The proxy object may hand out a reference to the actual object right before destroying it. This can happen when the actual class type is specified explicitly on the left hand-side:

C c = scoped!C(); // ← BUG c.foo(); // ← Accesses a destroyed object

In that definition, c is not the proxy object; rather, as defined by the programmer, a class variable referencing the encapsulated object. Unfortunately, the proxy object that is constructed on the right-hand side gets terminated at the end of the expression that constructs it. As a result, using c in the program would be an error, likely causing a runtime error:

Segmentation fault

For that reason, do not define scoped() variables by the actual type:

C a = scoped!C(); // ← BUG auto b = scoped!C(); // ← correct const c = scoped!C(); // ← correct immutable d = scoped!C(); // ← correct



Download



Copyright Disclaimer:
This site does not store any files on its server. We only index and link to content provided by other sites. Please contact the content providers to delete copyright contents if any and email us, we'll remove relevant links or contents immediately.