Reference counting pointer our savior?Filed Under: Weekly Tuesday Dose of goodness
Dear all,
I’m currently in KL working now. However, I’ve managed to eek out some time to make a post anyway.
This week I’ll be talking about one of the most commonly used savior - reference counting pointers!
I’ll be straight with you all, reference counting pointer in most cases, can resolve most of your memory issues. If used properly with data structures, they can easily prove to be one of the most invaluable weapons at your disposal.
However, even with all that, is reference counting pointer our sole savior?
Let’s find out.
Introduction
Just for the sake of those who doesn’t understand or know what’s a reference counting pointer, I shall spend some time here explaining what it is.
First of all, it is a template class usually. Unless we’re talking about COM which we’re not, otherwise we’re looking at templates, aka generics, aka that guy with a <T> somewhere.
I’d suggest that if you do not understand templates at all, please go read up on how to use them in the most basic manner before continuing. We’re not talking about implementing templates, rather, it’s about using them in general.
How does it work?
Well well well, by right this is one of the topics taught in one of my training class. So it’s probably not very convenient for me to talk about it so openly.
Technically, it works by counting the number of containers holding the same pointer. Once the number hits 0, the pointer is then deleted.
Examples? Well… see paragraph one
How to use it?
There’re many implementations of reference counting pointers. One of the most popular one is from the boost library. The reference counting pointer is known as boost::shared_ptr<T>.
All you need to do is to specialize T with a type and it can be used immediately.
For Strides Interactive, I developed my own reference counting pointer container with additional features that shared_ptr<T> doesn’t have. This, I’ll talk about it later if possible.
For StridesLib’s version which is downloadable from the downloads site, it works the same as well:
cSmartPtr<T>
After specialization:
cSmartPtr< cMyObject > objPtr = new cMyObject();
when I’m done…
objPtr = NULL;
it’s that simple - you can place a breakpoint at the destructor of your class and you’ll see it be called the moment you set the container to NULL.
Is a reference counting pointer a POINTER * ?
The answer is - generally, NO.
A reference counting pointer, as you can see in the example above, is a container that contains your pointer. By itself, it’s a stack object or a member attribute.
It passes itself around by value, exactly the way a pointer would. A pointer is an entity by itself, passing them around, you’re actually copying the address of the pointer all over the place. The same applies to reference counting pointer.
Just one thing - <T> in this case is always regarded as a pointer!!!
Unlike stl data structures where T is regarded as it is, a reference counting pointer doesn’t perform pointer arithmetics for you. It automatically assumes that T is a pointer type no matter what.
Problems
Now let’s talk about the problems imposed by reference counting pointers. For the same exact reason that it’s used to keep track of objects and guarantee that they’re alive until nobody else needs it, it also create a set of problems which require a different kind of solution to resolve.
For one, it causes the pointer to be locked inside, unless you can access it using a .ptr() function. This prevents the reference counting pointer to be passed into without additional access operations, thus making the codes look messy for Object-Oriented operations.
For example, a cSmartPtr< MyChild > is not an instance of MyChild*
Therefore when you try to call a method like - void method(const MyParent* parmBasePtr); - there’ll be compiler errors if you pass in cSmartPtr< MyChild >.
To go around, it’ll look something like this:
cSmartPtr< MyChild > child = new MyChild(); method(child.ptr());
The next problem occurs due to the nature of reference counting pointers. By itself, by default, it’ll manage the lifespan of an object for you entirely.
That means you can’t arbitrarily delete an object just like that. If you do, someone with the same container might end up accessing a dangling pointer instead.
The only way to delete a pointer legally is to set the reference counting container to NULL. Once the reference count reaches 0, only then the object is deleted.
In terms of timing, a reference counting pointer might not always be a good solution.
Moreover, in some cases, a reference counting pointer can cause Memory Hogging as well. Since the object in memory is kept alive until the final reference is released.
Conclusion
In conclusion, a reference counting pointer container saves us a lot of memory problems but also introduces a new set of problems that we have to tackle.
The commerical version of cSmartPtr<T> allows users to arbitrary delete the object by calling a .terminate() method. The carrot to this feature is that, it also guarantees that all other containers will never get a dangling pointer. Instead, they’ll return a null pointer when queried.
This allows you to control your memory properly and prevents access violation incidents from happening.
One of the biggest problems with reference counting pointers is that when it’s shared across several threads and processes, the final delete may be called by a member whose scope is not the same as the one used during its initial instantiation.
Such forms of deletes can easily incur an access violation and its origins will be extremely hard to trace and debug. Even if you find it, it’ll make almost absolutely no sense.
Bottomline, we must know what we’re using. Not only its strength but also its limitations so that we know what to avoid and look out for.
Again, hopefully this has been an informative article for you guys.
Enjoy your week ahead!
Signing off,
Jeremy
- Permalink
- Admin
- 23 Mar 2010 8:50 AM
- Comments (0)