Producing Singleton Garbage

Note: Most of this page is about pointer management in CeePlusPlus.

[From ClassicOoAntiPatterns. See also ClassesWithoutOo.]

You have CeePlusPlus as your first ObjectOrientedLanguage. You get so sick of trying to handle object references without GarbageCollection that you end up making many classes into Singletons. Then you discover that singletons, being not-very-cleverly-disguised globals, make the code hard to refactor. Later you discover ReferenceCounting ("smart") pointers and spend a week building and testing a SmartPointer template [this being pre-STL]. You think you're home free, but then you get caught by circular references. Finally, you find a language that has real GC and stuff C++ away in a closet. -- WayneConrad

The StandardTemplateLibrary has no ReferenceCounting or list-based SmartPointer. AutoPtr is merely an ownership passing pointer. It's only good for locals to do automatic cleanup and to protect you from exceptions. If you use an auto_ptr as a member variable, you're asking for pain.

Note that making every module a singleton reduces the modularization to CeeLanguage-style file modules, which certainly was workable beforehand, if not the best practice. What did you forget since then?

Besides, why aren't you using the constructor and destructor properly? CeePlusPlus doesn't need magic GarbageCollector threads to do its dirty work. You know exactly at what point objects will be destroyed and you can easily clean up. Resource leaks aren't as common as non-C++ people assume they are. Ohhhh yes they are. My own experience is that *every* commercial project whose primary language is C or C++, as it tends toward its (desired) release date, is entirely dominated by finding and fixing memory management bugs. Eventually, the team sets a threshold of pain, and will ship knowing that memory management bugs exist, but are hit "rarely enough". -- ArlieDavis

Moreover, even so-called GarbageCollected languages have ResourceLeaks, if not memory leaks. Any operation that needs to be balanced will fail. So, no magic bullet there. Indeed, with CeePlusPlus using the constructor/destructor to balance actions automatically is a common idiom and one that I sorely miss in JavaLanguage.

And let's not forget that GarbageCollectors lure you into a false sense of security where you may fail to clear references to objects, or let references hang while a thread is busy. This causes memory leaks. Indeed, if you lose control of references in a non-garbage-collected language, you only leak one node, whereas in a GarbageCollected language, you lose the entire subgraph rooted at that node.

GarbageCollectors only permit you to lose track of the nodes; you still have to keep track of the edges. There's an article on this in DrDobbsJournal Feb2000, if you're interested. -- SunirShah

Actually, STL doesn't have an auto_ptr class either. The AutoPtr class (a SmartPointer with exclusive-owner semantics) is part of the CeePlusPlus "Standard Library" - which is very different from Stepanov's "StandardTemplateLibrary". Of course, much, but not all of STL was added to the existing CeePlusPlus "Standard Library". For what it's worth, at one time the "Standard Library" was considering a template class named counted_ptr. This had a similar interface to auto_ptr but used "shared-owner" instead of "exclusive-owner" semantics. Since it was not added, most programmers have rolled their own counted_ptr that looks (but does not act) like auto_ptr. -- RobertDiFalco [See also CppCountedPointerImplementation]

I thought there was a reference-counted pointer in STL - I jumped to JavaLanguage before STL became big; that's my excuse for not knowing. Thanks for setting me straight.

What I'm really talking about here is spending over half my time at the computer dealing with CeePlusPlus pointers - making sure that things are freed in the destructor (and did I remember to make the destructor virtual), making sure that pointers are set to null or otherwise initialized in the constructor, making sure that the right thing is done with pointers when the object is assigned to, trying to figure out which of the 4 objects holding a pointer 'owns' the pointer and is therefore responsible for freeing its memory, spending late nights at the terminal trying to figure out which of the above I screwed up, causing an abend at some point far, far away from where the actual programming error was made. Sunir, it's not that I couldn't do it right - I got pretty good at it - it's that I started to resent how much time it was taking to do it right, time that should have been spent solving the customer's problems, not the language's problems. -- WayneConrad

All languages have trade-offs. There is a lot in CeePlusPlus that is clearly easier to do than in the JavaLanguage and vice versa. A good example is that unpredictable monstrosity called finalize for releasing resources or the inability to make an instance of an immutable class, constant by modifier. There are no universal truths in software development, each tool has its place--and it's here that Sunir and Wayne go off course trying to pit one against the other, as if they aren't apples and oranges. One cannot discount a GarbageCollected language any more than a non-garbage collected language, any more than I type safe, dynamically-bound, or statically-bound language. And, for what it's worth, there are many packages that provide GarbageCollection to CeePlusPlus. Of course, if you are doing all this to CeePlusPlus, it probably isn't the appropriate language choice for the job--i.e. just use a higher-level language.

However, I think if the subject is ProducingSingletonGarbage, then the above example isn't a good one. I don't think I even agree with the moniker. Personally, I would do something more language-neutral and rename the OO AntiPattern to something like UsingSingletonToHideGlobalData where, in any O-O language, one uses the SingletonPattern as a global lexical scope instead of a way to enforce a single instance of some class. As you know, there are many pages here on Wiki that criticize this use of the SingletonPattern to dress up the use of Globals. -- RobertDiFalco

By the way, I seem to have gotten away from my original point and into an anti-CeePlusPlus rant. I was saying that overuse of SingletonPattern was a mistake I fell into, and if we can confirm that others have fallen into that same mistake, then it can qualify as one of the ClassicOoAntiPatterns. -- WayneConrad

In the LispLanguage world, the standard idiom to manage your resources is to have a macro, say WITH-SOME-RESOURCE, used thusly:
	(with-some-resource (resource parameters)
and the macro ensures that the resource is released when execution leaves its body (via an UNWIND-PROTECT). We do this with locks, databases, file-handles, etc. Simple, elegant, and correct. I thought I'd miss CeePlusPlus's destructors, but I was way wrong. -- AlainPicard

On the subject of finalizers and managing resources that the GarbageCollection doesn't know about, see also ResourceAcquisitionIsInitialization, DeterministicResourceManagement, and ReleasingResourcesInJava. UNWIND-PROTECT is more or less try/finally with no catch - but without macros it remains tedious to do it properly.

RubyLanguage uses the "with-some-resource" idiom with blocks. In Ruby, a resource class provides a class-method that takes a block. This method allocates a resource object, passes the object to the block, and then releases the object after control has returned from the block. Classes often overload their instantiator methods to return a new object if called without a block, or to use the "with-some-resource" idiom if called with a block. E.g.

 # Allocate and return a file object:
 file = "some_file.txt", "r" )

# Allocate a file, pass it to the block, close the file after the block "some_file.txt", "r" ) do |file| process( file ) end

The semantics of ref-counted pointers can be complicated, which is why the standard library doesn't include one. There's one (more than one, actually) in the fabulous BoostLibraries. AndreiAlexandrescu's Loki library has some (very neat) ones too, see ModernCeePlusPlusDesign.

DeeLanguage has the best of both worlds when it comes to resource management, imo, since it has both GC (which you can easily sidestep if you need to, like when you need to pass a pointer to a C library) and automatic objects you can use for RAIA. D is very much like "A better C++ for the people who actually like C++" and I'd be happy if it managed to get some more mind share.

See also SingletonPattern, SingletonsAreEvil


View edit of December 28, 2005 or FindPage with title or text search