Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

From what I can see, there are two pervasive forms of resource-management: deterministic destruction and explicit. Examples of the former would be C++ destructors and smart pointers or Perl's DESTROY sub, whilst an example of the latter would be Ruby's blocks-to-manage-resources paradigm or .NET's IDispose interface.

Newer languages seem to opt for the latter, perhaps as a side-effect of using garbage collection systems of the non-reference-counting variety.

My question is this: given that destructors for smart pointers or reference-counting garbage collection systems -- almost being the same thing -- allow implicit and transparent resource destruction, is it a less leaky abstraction than the non-deterministic types which rely on explicit notation?

I'll give a concrete example. If you have three C++ subclasses of a single superclass, one may have an implementation that doesn't need any specific destruction. Perhaps its does its magic in another way. The fact that it doesn't need any special destruction is irrelevant -- all of the subclasses are still used in the same way.

Another example uses Ruby blocks. Two subclasses need to free resources, so the superclass opts for an interface that uses a block in the constructor, even though other specific subclasses might not need it since they require no special destruction.

Is it the case that the latter leaks implementation details of the resource destruction, whilst the former does not?

EDIT: Comparing, let's say, Ruby to Perl might be more fair since one has deterministic destruction and the other hasn't, yet they're both garbage-collected.

share|improve this question
4  
I am tempted to say "yes", but I like to hear what others have to say on this. –  Bart van Ingen Schenau Mar 12 '13 at 7:34
    
Transparent resource destruction? Apart from the fact that you have to use smart pointers instead of normal pointers? I do not think this is more transparent than having just one mechanism (references) to access objects (in C++ you have at least four or five). –  Giorgio Mar 12 '13 at 8:39
    
@Giorgio: "Ways to access an object" is quite vague. Do you mean read or write? Const/Volatile qualification? Pointers are not really "a way to access an object"; pretty much any expression results in an object and dereferencing a pointer just isn't that special. –  MSalters Mar 12 '13 at 10:37
    
@Giorgio: In that OOP sense, you can't send a message to a C++ pointer. You need to dereference the pointer to send the message (*ptr).Message() or equivalently ptr->Message(). There's an infinite set of allowed expressions, as ((*ptr))->Message is also equivalent. But they all boil down to expressionIdentifyingAnObject.Message() –  MSalters Mar 12 '13 at 10:48
1  
With refcounting you need to be careful about avoiding circles. So that abstraction leaks as well, just in a different way. –  CodesInChaos Mar 12 '13 at 16:43

3 Answers 3

up vote 2 down vote accepted

Your own example answers the question. The transparent destruction is clearly less leaky than explicit destruction. It can leak, but is less leaky.

Explicit destruction is analogous to malloc/free in C with all the pitfalls. Maybe with some syntactic sugar to make it appear scope-based.

Some of the benefits of transparent destruction over explicit:
--same usage pattern
--you can't forget to release the resource.
--clean up details do not litter the landscape at the point of usage.

share|improve this answer

No" the destructor, or other interface that says "this class must be destroyed" is a contract of that interface. If you make a subtype that doesn't require special destruction I would be inclined to consider that a violation of the Liskov Substitution Principle.

As for C++ vs. others, there's not much difference. C++ forces that interface on all it's objects. Abstractions can't leak when they're required by the language.

share|improve this answer
4  
"If you make a subtype that doesn't require special destruction" That's not a LSP violation, since no-op is a valid special case of destruction. The problem is when you add the destruction requirement to a derived class. –  CodesInChaos Mar 12 '13 at 16:44
    
I'm getting confused here. If one needs to add special destruction code to a C++ subclass, it doesn't change its usage patterns at all, because it's automatic. That means the superclass and subclass can still be used interchangeably. But with explicit notation for resource-management, a subclass needing explicit destruction would make its usage incompatible with a superclass, wouldn't it? (Assuming the superclass didn't need explicit destruction.) –  ljackman Mar 12 '13 at 17:19
    
@CodesInChaos - ah yes, I suppose that's true. –  Telastyn Mar 12 '13 at 18:11
    
@ljackman: A class which requires special destruction imposes a burden on whoever calls its constructor to ensure that it gets carried out. This does not create an LSP violation since a DerivedFooThatRequiresSpecialDestruction can only be created by code which calls new DerivedFooThatRequiresSpecialDestruction(). On the other hand, a factory method which returned a DerivedFooThatRequiresSpecialDestruction to code that wasn't expecting something requiring destruction, would be an LSP violation. –  supercat Feb 18 at 0:09

The failure in the abstraction is actually not the fact that garbage-collection is non-deterministic, but rather in the idea that objects are "interested" in things that they hold references to, and are not interested in things to which they do not hold references. To see why, consider the scenario of an object which maintains a counter of how often a particular control is painted. On creation, it subscribes to the control's "paint" event, and on disposal it unsubscribes. The click event simply increments a field, and a method getTotalClicks() returns the value of that field.

When the counter object is created, it must cause a reference to itself to be stored within the control it's monitoring. The control really doesn't care about the counter object, and would be just as happy if the counter object, and the reference to it, ceased to exist, but as long as the reference does exist it will call that object's event handler every time it paints itself. This action is totally useless to the control, but would be useful to anyone who would ever call getTotalClicks() on the object.

If e.g. a method were to create a new "paint-counter" object, perform some action on the control, observe how many times the control was repainted, and then abandon the paint-counter object, the object would remain subscribed to the event even though nobody would ever care if the object and all references to it simply vanished. The objects would not become eligible for collection, however, until the control itself is. If the method were one that would be invoked many thousands of times within the control's lifetime [a plausible scenario], it could cause a memory overflow but for the fact that the cost of N invocations would likely be O(N^2) or O(N^3) unless subscription-processing was very efficient and most operations didn't actually involve any painting.

This particular scenario could be handled by giving having the control keep a weak reference to the counter object rather than a strong one. A weak-subscription model is helpful, but doesn't work in the general case. Suppose that instead of wanting to have an object which monitors a single kind of event from a single control, one wanted to have an event-logger object which monitored several controls, and the system's event-handling mechanism was such that each control needed a reference to a different event-logger object. In that case, the object linking a control to the event logger should remain alive only as long as both the control being monitored and the event logger remain useful. If neither the control nor the event logger holds a strong reference to the linking event, it will cease to exist even though it's still "useful". If either holds a strong event, the lifetime of the linking object may be uselessly extended even if the other one dies.

If no reference to an object exists anywhere in the universe, the object may safely be considered useless and eliminated from existence. The fact that a reference exists to an object, however, does not imply that the object is "useful". In many cases, the actual usefulness of objects will depend upon the existence of references to other objects which--from the GC perspective--are totally unrelated to them.

If objects are deterministically notified when nobody is interested in them, they will be able to use that information to make sure that anyone who would benefit from that knowledge is informed. In the absence of such notification, however, there is no general way to determine what objects are considered "useful" if one knows only the set of references which exist, and not the semantic meaning attached to those references. Thus, any model which assumes that the existence or non-existence of references is sufficient for automated resource management would be doomed even if the GC could instantly detect object abandonment.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.