views:

235

answers:

3

Hi all,

Background: When reading Dr. Stroustrup's papers and FAQs, I notice some strong "opinions" and great advices from legendary CS scientist and programmer. One of them is about shared_ptr in C++0x. He starts explaining about shared_ptr and how it represents shared ownership of the pointed object. At the last line, he says and I quote:

. A shared_ptr represents shared ownership but shared ownership isn't my ideal: It is better if an object has a definite owner and a definite, predictable lifespan.

My Question: To what extent does RAII substitute other design patterns like Garbage Collection? I am assuming that manual memory management is not used to represent shared ownership in the system.

+6  A: 

The job of a programmer is to express things elegantly in his language of choice.

C++ has very nice semantics for construction and destruction of objects on the stack. If a resource can be allocated for the duration of a scope block, then a good programmer will probably take that path of least resistance. The object's lifetime is delimited by braces which are probably already there anyway.

If there's no good way to put the object directly on the stack, maybe it can be put inside another object as a member. Now its lifetime is a little longer, but C++ still doe a lot automatically. The object's lifetime is delimited by a parent object — the problem has been delegated.

There might not be one parent, though. The next best thing is a sequence of adoptive parents. This is what auto_ptr is for. Still pretty good, because the programmer should know what particular parent is the owner. The object's lifetime is delimited by the lifetime of its sequence of owners. One step down the chain in determinism and per se elegance is shared_ptr: lifetime delimited by the union of a pool of owners.

But maybe this resource isn't concurrent with any other object, set of objects, or control flow in the system. It's created upon some event happening and destroyed upon another event. Although there are a lot of tools for delimiting lifetimes by delegations and other lifetimes, they aren't sufficient for computing any arbitrary function. So the programmer might decide to write a function of several variables to determine whether an object is coming into existence or disappearing, and call new and delete.

Finally, writing functions can be hard. Maybe the rules governing the object would take too much time and memory to actually compute! And it might just be really hard to express them elegantly, getting back to my original point. So for that we have garbage collection: the object lifetime is delimited by when you want it and when you don't.


Sorry for the rant, but I think the best way to answer your question is context: shared_ptr is just a tool for computing the lifetime of an object, which fits into a broad spectrum of alternatives. It works when it works. It should be used when it's elegant. It should not be used if you have less than a pool of owners, or if you're trying to compute some sophisticated function using it as a convoluted way to increment/decrement.

Potatoswatter
@Potatoswatter Your answer stressed some serious points. The main point for me is the "gap" in ownership you talked about. Why would it happen? Isn't my program supposed to know what it is doing with resources it "requested". I would appreciate an example where RAII fails compared to Garbage Collection.
AraK
GCC uses garbage collection because it's more elegant and probably more efficient than dedicating time and possibly memory to tracking the multitude of objects it uses. Garbage collection is also well-suited to scripting languages where the user might implement a memory leak.
Potatoswatter
@Potatoswatter GCC is written in C not in C++, so the example is ansering the wrong question :)
AraK
Are you asking for a product which was written in C++ using RAII and failed as a result? ;v) My belief outlined above is that the middle ground between RAII and GC is new/delete. Maybe I should generalize and say that compilers aren't very amenable to RAII. I am aware of one compiler that did use reference counting for everything, and it wasn't very successful, so there you go. It predated C++, though.
Potatoswatter
+8  A: 

To what extent does RAII substitute other design patterns like Garbage Collection? I am assuming that manual memory management is not used to represent shared ownership in the system

Hmm, with GC, you don't really have to think about ownership. The object stays around as long as anyone needs it. Shared ownership is the default and the only choice.

And of course, everything can be done with shared ownership. But it sometimes leads to very clumsy code, because you can't control or limit the lifetime of an object. You have to use C#'s using blocks, or try/finally with close/dispose calls in the finally clause to ensure that the object gets cleaned up when it goes out of scope.

In those cases, RAII is a much better fit: When the object goes out of scope, all the cleanup should happen automatically.

RAII replaces GC to a large extent. 99% of the time, shared ownership isn't really what you want ideally. It is an acceptable compromise, in exchange for saving a lot of headaches by getting a garbage collector, but it doesn't really match what you want. You want the resource to die at some point. Not before, and not after. When RAII is an option, it leads to more elegant, concise and robust code in those cases.

RAII is not perfect though. Mainly because it doesn't deal that well with the occasional case where you just don't know the lifetime of an object. It has to stay around for a long while, as long as anyone uses it. But you don't want to keep it around forever (or as long as the scope surrounding all the clients, which might just be the entirety of the main function).

In those cases, C++ users have to "downgrade" to shared ownership semantics, usually implemented by reference-counting through shared_ptr. And in that case, a GC wins out. It can implement shared ownership much more robustly (able to handle cycles, for example), and more efficiently (the amortized cost of ref counting is huge compared to a decent GC)

Ideally, I'd like to see both in a language. Most of the time, I want RAII, but occasionally, I have a resource I'd just like to throw into the air and not worry about it any more and trust that it'll get cleaned up when it's safe to do so.

jalf
nice answer. ++
Eli Bendersky
+1  A: 

Is Garbage Collection a Design Pattern? I don't know.

The big advantage of shared ownership, is it's inherent predictability. With GC the reclamation of resources is out of your hands. Thats the point. When, and how it happens is usually not on the mind of the developer using it. With shared ownership, you are in control (beware, sometimes too much control is a bad thing). Lets say your app spawns off a million shared_ptr's to X. All of those are your doing, you are responsible for them and you have total control over when those references are created and destroyed. So a determined and careful programmer should know exaclty who references what and for how long. If you want an object to be destroyed, you have to destroy all the shared references to it, and viola, it's gone.

This carries some profound consequences for people who make realtime software, which MUST be totally predictable. This also means you can fudge up in ways that look an awful lot like memory leaks. I personally don't want to be a determined and careful programmer when I don't have to be (go ahead and laugh, I want to go on picnics and bike rides, not count my references), so where appropriate GC is my prefered route. I have written a little bit of realtime sound software, and used shared references to manage resources predictably.

Your question: When does RAII fail? (In the context of shared references) My Answer: When you can't answer the question: who may have a reference to this? When vicious insipid circles of ownership develop.

My question: When does GC fail? My answer: When you want total control and predictability. When the GC is the written by Sun Microsystems in a last minute dash to deadline and has ridiculous behaviors which could only have been designed and implemented by severely drunk protohuman code monkeys borrowed from Microsoft.

My opinion: I think BS is just really serious about clear design. It seems obvious that having one place where resources are destroyed is usually a clearer design than having many places where they might destroyed.