C++11 Standard — What are Smart Pointers good for?
What is a shared_ptr?
A common problem in larger C++ projects is memory management. When a pointer is being passed from function to function, who has the responsibility of deleting the data at the end? I suppose you can come up with a few rules that can be followed to help manage this sort of delegation, but to solve this problem there’s the idea of shared pointers, otherwise known smart pointers. The shared_ptr is a way of giving new pointers ‘ownership’ over a piece of memory, so long as there is someone claiming ‘ownership’, the memory will not be deallocated.
Is this really a good idea?
“Use shared_ptr as a last resort”
-Bjarne Stroustrup
I definitely agree with Bjarne on this one, but I would also argue that it strongly depends on your coding style. Having this sort of system in place and making it common practice in your code-base could encourage an excess of shared ownership. Code with less shared ownership will definitely lead to an easier to comprehend code-base and far less bugs, this idea goes well with the idea of OOP de-coupling principles. Of course, shared ownership most of the time is something we just can not avoid; but it’s something we should actively try to avoid. Consider the following alternative — C++11 also supports scoped_ptr which automatically deallocates memory for you, similar to a shared_ptr, except that copying the memory address off the pointer yields a compile error!
The basic shared_ptr explanation, for the uninformed
C++ TR1 introduced the shared_ptr (from the boost library). You can think of this as a ‘reference counted pointer‘. Every time that someone takes ‘ownership‘ of this pointer, a counter is incremented in the background. Every time this pointer goes out of scope or is explicitly ‘reset‘ to null, the counter is decremented. If this counter ever reaches 0, the memory that it references is automatically deleted. This means that if we diligently use shared_ptr to hold all of our pointers, we can easily avoid memory leaks which are caused by letting pointers that go out of scope or are overwritten (of course, smart pointers will ensure that the proper virtual functions are called but they can’t protect you if destructors are not properly implemented!)
Here’s a minimal example of a shared_ptr:
#include <memory>
using namespace std;
void my_function() {
shared_ptr<int> my_ptr(new int(9001));
shared_ptr<int> also_ptr = my_ptr;
// Internal counter is 2, the integer will not be deleted from memory
// Until both of the shared_ptrs have been .reset() or go out of scope.
my_ptr.reset();
also_ptr.reset();
// Note: even if we did not call reset, both of the shared_ptrs going
// out of scope (end of function), would have caused an auto-deletion!
}
There are many nuances to using shared_ptrs, the most common one is circular references. In the case of having objects which contain pointers which eventually point back to themselves (back-reference), a shared_ptr can’t handle this because deletion, it just doesn’t know what to do. You fix this by making ‘back-references’ weak_ptrs, this means that when you assign the back-reference, it is not reference counted.
Sources:
http://programmers.stackexchange.com/questions/133302/stdshared-ptr-as-a-last-resort
http://www.codeproject.com/Articles/8394/Smart-Pointers-to-boost-your-code
http://www.boost.org/doc/libs/1_51_0/libs/smart_ptr/shared_ptr.htm
http://www.boost.org/doc/libs/1_51_0/libs/smart_ptr/weak_ptr.htm