56

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?

hyde
  • 3,754

5 Answers5

60

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 on desktops or lightly loaded servers, but for other resources (e.g. file handles) you often need them be closed ASAP to avoid potential conflicts later on.

  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++ 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 well for your use case -- 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".

28

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.

4

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

True but, objectively, the vast majority of code is now written in modern languages with tracing garbage collectors.

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.

That's a bad idea because you still need to worry about cycles.

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?)

Oh wow, there are so many things wrong with your line of thinking:

  1. Boehm's GC is not a "proper" GC in any sense of the word. It is truly awful. It is conservative so it leaks and is inefficient by design. See: http://flyingfrogblog.blogspot.co.uk/search/label/boehm

  2. Shared pointers are, objectively, nowhere near as popular as GC because the vast majority of developers are using GC'd languages now and have no need of shared pointers. Just look at Java and Javascript in the job market compared to C++.

  3. You appear to be restricting consideration to C++ because, I assume, you think that GC is a tangential issue. It isn't (the only way to get a decent GC is to design the language and VM for it from the beginning) so you are introducing selection bias. People who really want proper garbage collection don't stick with C++.

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

You are restricted to C++ but wish you had automatic memory management.

J D
  • 2,332
3

In MacOS X and iOS, and with developers using Objective-C or Swift, reference counting is popular because it is handled automatically, and the use of garbage collecting has considerably decreased since Apple doesn't support it anymore (I am told that apps using garbage collection will break in the next MacOS X version, and garbage collection was never implemented in iOS). I actually seriously doubt that there was ever much software using garbage collection when it was available.

The reason for getting rid of garbage collection: It never worked reliably in a C-style environment where pointers could "escape" to areas not accessible by the garbage collector. Apple strongly believed and believes that reference counting is faster. (You can make any claims here about relative speed, but nobody has been able to convince Apple). And in the end, nobody used garbage collection.

First thing that any MacOS X or iOS developer learns is how to handle reference cycles, so that's not a problem for a real developer.

gnasher729
  • 49,096
2

The biggest disadvantage of garbage collection in C++ is, that it's plain impossible to get right:

  • In C++, pointers do not live in their own walled community, they are mixed with other data. As such, you can't distinguish a pointer from other data that just happens to have a bit pattern that can be interpreted as a valid pointer.

    Consequence: Any C++ garbage collector will leak objects that should be collected.

  • In C++, you can do pointer arithmetic to derive pointers. As such, if you don't find a pointer to the start of a block, that does not mean that that block can't be referenced.

    Consequence: Any C++ garbage collector has to take these adjustments into account, treating any bit sequence that happens to point anywhere within a block, including right after the end of it, as a valid pointer that references the block.

    Note: No C++ garbage collector can handle code with tricks like these:

    int* array = new int[7];
    array--;    //undefined behavior, but people may be tempted anyway...
    for(int i = 1; i <= 7; i++) array[i] = i;
    

    True, this invokes undefined behavior. But some existing code is more clever than is good for it, and it may trigger preliminary deallocation by a garbage collector.