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.

  • 5
    I am tempted to say "yes", but I like to hear what others have to say on this. Commented Mar 12, 2013 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
    Commented Mar 12, 2013 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
    Commented Mar 12, 2013 at 10:37
  • 1
    @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
    Commented Mar 12, 2013 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. Commented Mar 12, 2013 at 16:43

4 Answers 4


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.


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.


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.

  • 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. Commented Mar 12, 2013 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.) Commented Mar 12, 2013 at 17:19
  • @CodesInChaos - ah yes, I suppose that's true.
    – Telastyn
    Commented Mar 12, 2013 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
    Commented Feb 18, 2014 at 0:09

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?

Having to watch for cycles by hand is neither implicit nor transparent. The only exception is a reference counting system with a language that prohibits cycles by design. Erlang might be an example of such a system.

So both approaches leak. The main difference is that destructors leak everywhere in C++ but IDispose is very rare on .NET.

  • 1
    Except cycles are exceedingly rare and practically never occur except in data structures that are explicitly cyclic. The main difference is that destructors in C++ are handled properly everywhere but IDispose rarely handles the problem in .NET.
    – DeadMG
    Commented May 31, 2015 at 9:36
  • "Except cycles are exceedingly rare". In modern languages? I would challenge that hypothesis.
    – J D
    Commented Jun 1, 2015 at 8:39

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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