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.

As I can see, smart pointers are used extensively in many real-world C++ projects.

Though some kind of smart pointers are obviously beneficial to support RAII and ownership transfers, there is also a trend of using shared pointers by default, as a way of "garbage collection", so that the programmer do not have to think about allocation that much.

Why are shared pointers more popular than integrating a proper garbage collector like Boehm GC? (Or do you agree at all, that they are more popular than actual GCs?)

I know about two advantages of conventional GCs over reference-counting:

  • Conventional GC algorithms has no problem with reference-cycles.
  • Reference-count is generally slower than a proper GC.

What are the reasons for using reference-counting smart pointers?

share|improve this question

migrated from stackoverflow.com Aug 14 '13 at 2:29

This question came from our site for professional and enthusiast programmers.

2  
I'd just add a comment that this is a wrong default to use: in most cases, std::unique_ptr is sufficient and as such has zero-overhead over raw pointers in terms of run-time performance. By using std::shared_ptr everywhere you'd also obscure the ownership semantics, losing one of the major benefits of smart pointers other than automatic resource management -- clear understanding of the intent behind the code. –  Matt Aug 14 '13 at 17:32

2 Answers 2

up vote 35 down vote accepted

Some advantages of reference counting over garbage collection:

  1. Low overhead. Garbage collectors can be quite intrusive (e.g. making your program freeze up at unpredictable times while a garbage collection cycle processes) and quite memory-intensive (e.g. your process's memory footprint unnecessarily grows to many megabytes before garbage-collection finally kicks in)

  2. More predictable behavior. With reference counting, you are guaranteed that your object will be freed the instant the last reference to it goes away. With garbage collection, on the other hand, your object will be freed "sometime", when the system gets around to it. For RAM this isn't usually a big problem, but for other resources (e.g. file handles) you often want them to be closed ASAP.

  3. Simpler. Reference counting can be explained in a few minutes, and implemented in an hour or two. Garbage collectors, especially ones with decent performance, are extremely complex and not many people understand them.

  4. Standard. C++'s includes reference counting (via shared_ptr) and friends in the STL, which means that most C++ programmers are familiar with it and most C++ code will work with it. There isn't any standard C++ garbage collector, though, which means that you have to choose one and hope it works -- and if it doesn't, it's your problem to fix, not the language's.

As for the alleged downsides of reference counting -- not detecting cycles is an issue, but one that I've never personally ran into in the last ten years of using reference counting. Most data structures are naturally acyclical, and if you do come across a situation where you need cyclical references (e.g. parent pointer in a tree node) you can just use a weak_ptr or a raw C pointer for the "backwards direction". As long as you are aware of the potential problem when you're designing your data structures, it's a non-issue.

As for performance, I've never had a problem with the performance of reference counting. I have had problems with the performance of garbage collection, in particular the random freeze-ups that GC can incur, to which the only solution ("don't allocate objects") might as well be rephrased as "don't use GC".

share|improve this answer
8  
Naïve reference-counting implementations typically get much lower throughput than production GCs (30–40%) at the expense of latency. The gap can be closed with optimisations such as using fewer bits for the refcount, and avoiding tracking objects until they escape—C++ does this last naturally if you mainly make_shared when returning. Still, latency tends to be the bigger problem in realtime applications, but throughput is more important generally, which is why tracing GCs are so widely used. I wouldn’t be so quick to speak badly of them. –  Jon Purdy Aug 14 '13 at 6:13
2  
I'd quibble 'simpler': simpler in terms of the total amount of implementation required yes, but not simpler for the code that uses it: compare telling someone how to use RC ('do this when creating objects and this when destroying them') to how to (naively, which is often enough) use GC ('...'). –  AakashM Aug 14 '13 at 8:08
2  
(although I'm probably hideously unaware of how easy smart pointers are to use in modern versions of C-ish languages) –  AakashM Aug 14 '13 at 9:00
    
C++ is incompatible with garbage collector. The C++11 specification undefined some behaviour to make boehm gc compliant, but IIRC there was some argument that it's still missing some bits. Does not explain why some other interpreters like perl or python hang on to the reference counting collectors (both have heap walk to deal with cycles too). –  Jan Hudec Aug 14 '13 at 12:52
    
@JanHudec Python (by which you mean CPython, I assume) hangs onto refcounting because every piece of code even slightly related to its API, including millions of lines of third party code, is completely dependent on refcounting and non-moving objects. –  delnan Aug 14 '13 at 13:41

To get good performance out of a GC, the GC needs to be able to move objects in memory. In a language like C++ where you can interact directly with memory locations, this is pretty much impossible. (Microsoft C++/CLR doesn't count because it introduces new syntax for GC-managed pointers and is thus effectively a different language.)

The Boehm GC, while a nifty idea, is actually the worst of both worlds: you need a malloc() that is slower than a good GC, and so you lose the deterministic allocation/deallocation behavior without the corresponding performance boost of a generational GC. Plus it is by necessity conservative, so it won't necessarily collect all your garbage anyway.

A good, well-tuned GC can be a great thing. But in a language like C++, the gains are minimal and the costs are often just not worth it.

It will be interesting to see, however, as C++11 becomes more popular, whether lambdas and capture semantics start to lead the C++ community towards the same kinds of allocation and object lifetime problems that caused the Lisp community to invent GCs in the first place.

See also my answer to a related question over on StackOverflow.

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.