Permission to read, use and copy
this documentation and accompanying software is hereby granted without fee,
provided that the above copyright notice appears in all copies and the contents
remains unmodified. Any recommended modification should be sent directly to the
author’s email address: tdemjen@yahoo.com.
The author makes no representations about the
suitability of the accompanying software for any purpose. It is provided
"as is" without expressed or implied warranty.
Smart pointers are an extraordinarily useful tool. They are a friend of every C++ programmer. They not only make programming easier, they also make applications safer and less buggy.
A smart pointer is a typical example of the “resource initialization is acquisition” idiom, which is just a fancy way of saying that resources are safer when they are encapsulated into classes. That is because the C++ language guarantees that the destructor of an object allocated on the stack is always called automatically, even if an exception occurs during execution. Consider the following example:
class Pointer
{
int*
value;
public:
Pointer() :
value(new int) { }
~Pointer() { delete
value; }
int*
get() const { return value; }
};
This sample class is a very thin wrapper around a pointer to an integer. It ensures that memory allocation and deletion are always performed in pair: For every allocation there is a matching deletion. Smart pointers have the exact same purpose as our little Pointer class above, except that production quality implementations are much more flexible. If you are using smart pointers, you will no longer forget about freeing your memory, and thus your accidental memory leaks will all disappear.
Smart pointers, however, are much more than fancy tools that free you from the burden of worrying about the deletion of your allocated memory. It is not that without smart pointers you would tend to forget about calling delete. The advantages are much more significant than that.
Most importantly, smart pointers foster exception safety. Without wrapping your pointers into trivial classes, the only way to ensure exception safety would be to use try / finally. That construct has several disadvantages, though. The finally keyword is not a part of the standard, and therefore not every compiler supports it, or those who do use different syntax (finally, or __finally). Moreover, a try block has a significantly bigger overhead than a lightweight auto pointer. Also, a try block is longer to type and is harder to read. But the biggest disadvantage of try is that it is extremely painful to nest them when you need to protect multiple variables. After a level of 2 or 3, the code becomes completely unreadable.
There are other reasons for using smart pointers than exception safety. Often objects are not deleted in the same function where they are allocated. It is a very usual practice for a function to return a pointer to an object, and it is almost always the caller’s responsibility to delete it when it is no longer used. If you maintain a complicated tree structure of objects, some objects pointing to many others, it is excessively complicated to take care of the object destruction in a manual fashion. Since the language does not enforce certain important rules regarding pointers, you consistently have to add comments or write documentation to indicate that a pointer is exclusively owned, shared, or weak, as well as who is responsible for deleting it. For example:
const char* s;
// this is a string, the object owns it and
// allocates it automatically
Object*
parent;
// this is a weak pointer, no ownership, never
// call delete on it
Widget* CreateNew(WidgetType type);
// the caller is responsible for allocating
the result
The problem with this is that programmers often don’t read these comments, don’t read the manuals, and tend to forget about these little details. But even if you always remember (which is certainly an extra effort and something additional to constantly worry about), you can accidentally delete an object twice, or let a pointer address a dead (already deleted) object. Furthermore, classic pointers must be initialized, and if you forget about that, they will point to a random address, causing crashes in an inconsistent manner. All of these bugs are very hard to detect and locate. Often such bugs remain unnoticed until the product is released.
The good news is that flexible smart pointer implementations, such as the boost::shared_ptr, solve all of the issues outlined above, without much hassle and without a significant overhead. Less flexible but lightweight auto pointers, such as boost::scoped_ptr, solve many of these problems with a near zero overhead (in release mode).
The std::auto_ptr has a tremendous disadvantage: It can not be copied without destruction. When you need to make a copy of an auto pointer, the original instance is destroyed. This means you may only have a single copy of the object at any time. This also means that auto_ptr can not be used with standard containers, such as vector, deque, list, set, and map. In fact, it can hardly be used in any class that relies on copy construction.
Furthermore, auto_ptr is not safe, because nothing prevents you from doing a copy accidentally. And if you do so, you destroy the original copy. Also, some less standard compliant C++ compilers let you store forward declared objects in an auto_ptr, and use that without ever including the full definition of the class. This always results in a memory leak.
The boost::shared_ptr is a popular reference counted auto pointer implementation. It means you can make any number of copies of an object stored inside a shared_ptr. Every time you make a copy, lazy evaluation is used, which means there is no real object assignment performed. In other words, shared_ptr does not allocate memory for the copy, nor does it call the copy constructor or the assignment operator. What it does, it simply copies the underlying plain pointer (32 bits of data on today’s PCs), and increases a variable called the reference counter. Even if you have multiple copies of a shared_ptr, you still have a single instance of your underlying object. As shared pointers go out of scope, the reference counter decreases. When the last shared_ptr holding your instance goes out of scope, your object is destructed automatically.
Shared pointers have a great advantage: They are widely recognized and accepted among the C++ community. Chances are good that other programmers you may work with in the near future are already familiar with them. This means you won’t have a hard time explaining your choice of using boost::shared_ptr to your teammates.
Before you use shared_ptr, you have to include the following header file:
#include
<boost/shared_ptr.hpp>
Creating a shared pointer is very easy. Here is an example that holds a pointer to an integer:
boost:shared_ptr<int> my_pointer(new int);
The syntax is always the same. It all begins with boost::shared_ptr, then the type name between angled brackets, then the variable name, and finally the object construction. The example above has a similar effect as the following code:
int*
my_pointer = new int;
[...]
delete my_pointer;
Using a shared pointer is almost as easy as using a conventional pointer. You can dereference it with the * or -> operator:
*my_pointer = 16;
cout
<< *my_pointer;
In order to improve the readability, in this article we are going to omit the std:: and boost:: namespace specifiers in most places, except the first time when we introduce a new Boost type of function. In practice, you can usually import the boost namespace in your CPP files, like this:
using namespace boost;
This prevents you from typing “boost::” everywhere, which would make your code unreasonably verbose. You already have to deal with readability issues, as typing shared_ptr<int> is much longer than typing int*, although it indicates the intention of the pointer much better. Nevertheless, you definitely would not like to make the declaration even longer by appending “boost::” everywhere. This is a personal preference, though, and some prefer to do so. We will get back to this topic a little bit later.
Once you have a shared_ptr, you can create a copy of it as you do it with a conventional pointer:
shared_ptr<int> second_pointer = my_pointer;
The actual integer value is not de-allocated until all copies of the shared pointer are gone out of scope.
If the need arises, you can assign another object to an existing shared_ptr:
second_pointer = some_other_pointer;
You can even create a new object, replacing the one currently held by the smart pointer:
second_pointer.reset(new int);
The “reset” member function removes the previously assigned object, if any, and replaces it with a new one. Of course, if there are no other copies, the previously held object is automatically destroyed. You can also use reset to clear your shared pointer:
second_pointer.reset(); // same as .reset(0);
This clears the previously held object and replaces it with a null pointer.
If a shared pointer is not initialized at creation time, it will automatically hold a null pointer:
shared_ptr<int> empty_pointer;
Later you can assign a value to it using the assignment operator (=), or calling reset(new int).
It is important to note that shared pointers can be returned from functions:
shared_ptr<int> GetValue()
{
return shared_ptr<int>(new int);
}
Not surprisingly, they can be passed to other functions as well:
bool
IsFive(shared_ptr<const int> input)
{
return *input == 5;
}
void Increase(shared_ptr<int> input)
{
++(*input);
}
Good programmers care about const-correctness, and therefore it is important to learn how to deal with constant pointer. A shared pointer to a constant object is expressed like this:
shared_ptr<const T>
This is similar to writing const T* in traditional C++. It allows you to change the shared pointer itself, but not the object it points to. If you need a pointer whose value can not change, declare the shared_ptr itself const:
const shared_ptr<int>
You can also use the combination of the two, thus making both the pointer and the pointed object constant:
const shared_ptr<const
int>
Also note that we don’t pass shared pointers by const reference; we pass them by value, like normal pointers. You would normally pass objects as a const reference or a const pointer to functions. Not shared_ptr, though, because that is little more than a thin wrapper around a traditional pointer. Of course, when you need to return a pointer via the argument list, you need to pass it by reference:
void GetValue(shared_ptr<int>&
output)
{
output.reset(new int);
*output = 5;
}
This is roughly equivalent to the following in traditional C++:
void GetValue(int* & output)
{
output = new int;
*output = 5;
}
Now that you are familiar with the basics, we can move on to more advanced topics.
You have seen so far that shared_ptr behaves pretty much like a conventional pointer, aside from the fact that its type is shared_ptr<T> instead of T*, and shared_ptr<const T> instead of const T*. In reality, when you are copying or dereferencing a shared pointer, you can almost forget that it is not a conventional pointer. Surprisingly, however, the similarities end here.
Of all the differences, the most important is that a shared pointer is not compatible with a plain pointer. You can not directly assign a T* to a shared_ptr<T>, or vice versa. The only two places where you can assign a traditional pointer to a shared_ptr are the constructor and the reset function:
shared_ptr<int> pointer(new int);
pointer.reset(new int);
You are not allowed to do a direct assignment:
pointer = new int;
// ERROR
Shared pointer constructors are explicit, which means you can not do this either:
shared_ptr<int>
pointer = new int; // ERROR
Also, you are not allowed to handle shared_ptr as if it were a plain pointer:
int*
p = pointer; // ERROR
If you need to have access to the underlying conventional pointer, use the get() member function:
int*
p = pointer.get();
However, it is not safe to keep this “p” variable in the memory for a long time. If you make any modification to “pointer”, “p” may be invalidated. The reason that shared_ptr has a get member function at all is to be able to pass an auto pointer to a conventional function that does not know about the Boost library:
extern void Function(int*
p);
Function(pointer.get());
It is reassuring to know that you can not accidentally delete an auto pointer:
delete pointer; // ERROR: call .reset()
instead!
Testing your pointer against 0 is a very typical chore. Fortunately, shared pointer has a tricky mechanism built inside that lets you test it against a bool:
if(pointer) { }
if(! pointer) { }
if(pointer == 0) { }
if(pointer != 0) { }
All of the four syntaxes above work fine, like if “pointer” were a traditional pointer.
Naturally, two shared pointers can be compared using operators ==, !=, and <. The operator > is not implemented, though. The only reason when you would want to use the < operator is when you store a shared_ptr in a sorted container, such as a set.
Before we go on, I would like to mention three more useful member functions:
bool
shared_ptr<T>::unique() const
This function makes sure that your smart pointer is detached from the other copies, thus forming a unique and independent copy of your object. Note that “unique” calls the copy constructor for T to make sure that a real copy is performed. You should always call “unique” before you modify an object in such a way that the other copies are not affected. Also, you must call “unique” before passing shared pointers in and out across threads.
long shared_ptr<T>::use_count() const
It returns the number of shared pointers that point to the same underlying T object.
void shared_ptr<T>::swap(shared_ptr<T>& other)
It swaps two shared pointers. It is also available as a global function:
void swap(shared_ptr<T>&
a, shared_ptr<T>& b)
All in all, shared pointers are almost always used to store class instances, not simple types. In this section, we are going to discuss some of the issues regarding storing classes in shared pointers.
One of the most important issues is casting. Generally speaking, if type T can be casted to type Y, you can assign shared_ptr<T> to shared_ptr<Y>:
shared_ptr<T> t;
shared_ptr<Y> y(t);
y = t;
y.reset(new T);
You can even assign an auto_ptr to a shared_ptr:
auto_ptr<T> a;
shared_ptr<T> t(a);
Very often you will store polymorphic classes in shared_ptr. Fortunately it is as easy as if you were using conventional pointers:
struct
B { virtual void f(); };
struct
D : public B { virtual void f(); };
shared_ptr<B> base_ptr(new D);
base_ptr->f();
The first big challenge regarding shared pointers is when you need to use dynamic_cast. First you might want to try something like this:
shared_ptr<D> d(dynamic_cast<shared_ptr<B>
>(base_ptr)); // ERROR
Surprisingly, this does not work. If you have used dynamic_cast before, you know that it has the following syntax:
D* d = dynamic_cast<D*>(base_ptr);
Note, however, that shared_ptr<D> is not descended from shared_ptr<B>, and therefore there is no inheritance relationship between them. In sort, the traditional dynamic_cast idiom does not work with auto pointers.
As a matter of fact, there is no way you can use dynamic_cast directly. To bridge the gap between shared_ptr<D> and D*, the author of shared_ptr provided boost::shared_dynamic_cast. It is a little tricky first, but you will get used to it:
shared_ptr<D> d(shared_dynamic_cast<D>(base_ptr));
Note how shared_dynamic_cast does not have shared_ptr in its template argument. The following would not compile:
shared_ptr<D> d(shared_dynamic_cast<shared_ptr<D>
>(base_ptr)); // ERROR
You may be curious what shared_dynamic_cast does internally. It is probably not surprising that it calls dynamic_cast inside. Here is how it works. If the cast is successful, “d” will hold a copy of “base_ptr”, interpreted as D*. If the cast fails, “d” will hold a null pointer. This behavior is in agreement with the original dynamic_cast, which also returns 0 when the cast fails.
There is another type of dynamic cast, however, which behaves slightly differently. It’s called boost::shared_polymorphic_cast. Internally it calls dynamic_cast as well, and when the cast succeeds, it works like shared_dynamic_cast. However, when the cast fails, shared_polymorphic_cast throws an exception of type std::bad_cast. It guarantees that the result can never be a null pointer:
class C { };
shared_ptr<C> d(shared_polymorphic_cast<C>(base_ptr));
// throws bad_cast
Sometimes dynamic_cast is an overkill, and you just want to perform a static_cast. Here’s how you would do it in conventional C++:
D* d = static_cast<D*>(base_ptr);
or the old-fashioned way:
D* d = (D*)base_ptr;
By this time you may have already figured it out that explicit casting wouldn’t work with shared_ptr directly. Fortunately, there’s a function called boost::shared_static_cast that comes to our help:
shared_ptr<D> d(shared_static_cast<D>(base_ptr));
This, of course, does not throw when base_ptr is 0. Internally it calls static_cast.
Sometimes it is a little hard to decide whether to use static_cast or dynamic_cast, and you wish you could have a little bit of both worlds. It is well known that dynamic_cast has a runtime overhead, but it is safer, whereas static_cast has no overhead at all, but it may fail silently. How nice it would be if you could use shared_dynamic_cast in debug builds, and shared_static_cast in release builds. Well, such a thing is already available and is called shared_polymorphic_downcast:
shared_ptr<D> d(shared_polymorphic_downcast<D>(base_ptr));
It is nothing else but a shared_static_cast, but it also performs an assertion internally, something like this:
assert(dynamic_cast<T*>(p.get())
== p.get())
In other words, when the NDEBUG symbol is not defined (during debugging), it double checks that the dynamic_cast is valid indeed. For the exact implementation, take a look at shared_ptr.hpp.
Finally, I would like to mention that shared_ptr can be stored inside STL containers without any problem:
list<shared_ptr<B> > objects;
shared_ptr<D> d(new
D);
objects.push_back(d);
A weak_ptr is used to break circular references. For example, if A has a shared pointer to B, B can not hold a shared_ptr to A. One of them must be a weak_ptr. The same is true with self pointers: A shared_ptr object should not have a shared_ptr to itself. These are the cases when weak_ptr are necessary.
weak_ptr is much better than a conventional pointer, because it is still hooked to the shared_ptr. If the shared_ptr gets destroyed, all weak pointers addressing the destroyed object automatically get zeroed out. So there are no more orphaned pointers, pointing to invalid memory locations!
It is easy to create a weak_ptr. It can only be created from an existing shared_ptr:
shared_ptr<T> t(new
T);
weak_ptr<T> weak_t(t);
As you see, the conversion is trivial. Now you have to understand that weak_ptr is just a very thin shell, and it can not be directly dereferenced:
weak_t->Execute();
// invalid!
Before you can use a weak pointer, you have to convert it into a full-blown shared_ptr, at least temporarily, like this:
shared_ptr<T> temp_access_t(weak_t);
temp_access_t->Execute();
This ensures that there is a temporary copy even if the original “t” object goes out of scope in the meantime.
I mentioned that weak_ptr is
necessary to resolve circular references. There are, however, many other cases
when you might want to consider using a weak pointer instead of a full shared_ptr. First of all, its name very clearly indicates
the intention of the programmer, making the code almost self-documenting. Second,
a weak_ptr does not hold a true copy of the object.
If all the shared pointers go out of scope, the object
they hold are destroyed, even if there are weak pointers referring to it. The
weak pointers automatically get zeroed out. If you used shared_ptr
instead of weak_ptr, the object it holds would still
remain in the memory. This means weak pointers are indeed weaker, and they
don’t require that the object they hold actually exists. When a shared_ptr is alive, you can be sure that the object it
holds is alive as well. With weak pointers, the object they hold is alive only
as long as there are shared pointers holding the object.
There are cases when you can’t avoid using the “this” pointer. It typically happens when in a tree structure you have to pass “this” to a parent, to indicate that we are a child, or the opposite, when we have to pass “this” to a child to indicate that we are the parent. Here is an example using conventional pointers:
class Object
{
Object* parent;
list<Object*>
children;
public:
void AddChild(Object* obj)
{
children.push_back(obj);
// add it to the children’s list
obj->parent
= this;
}
};
This is a typical recursive composition [GOF], which is a design pattern that implements an object tree. The naïve attempt to do this with shared pointers would be like this:
class Object
{
weak_ptr<Object>
parent;
list<shared_ptr<Object> > children;
public:
void AddChild(shared_ptr<Object>
obj);
};
The problem is how to pass the “this” pointer to a weak_ptr (or to a shared_ptr, for that matter):
void Object::AddChild(shared_ptr<Object> obj)
{
children.push_back(obj);
obj->parent
= weak_ptr<Object>(this); // not possible!!!
}
The “this” is a simple pointer, and normally you can not convert plain pointers into weak_ptr or shared_ptr. The problem is that we already have a shared_ptr<Object> instance existing in the memory, but the compiler-provided “this” pointer does not work, because there are no internal hooks between “this” and shared_ptr. Unfortunately, the compiler does not create an association between the “this” pointer and the shared_ptr that owns it. There does not seem to be a way of obtaining a shared_ptr from “this”.
This is indeed the biggest problem regarding shared pointers, and it can not be solved without significantly redesigning your class. Every time you have to pass the “this” pointer to a shared_ptr or weak_ptr, you have to think about that in advance, and incorporate it into your design. (Or, alternatively, refactor your lacking design). Here is how.
The solution is surprising, almost unbelievable at first. The key is to capture the shared_ptr right after an instance is constructed, and associate it with the object itself:
class Object
{
public:
weak_ptr<Object>
weak_this;
};
shared_ptr<Object> root(new
Object);
root->weak_this
= root;
This way the object will always know about “itself”, in the form of the “weak_this” pointer. Obviously this design is less than desirable, as weak_this always has to be initialized manually. It needs a dramatic twist before it can be used in a real word application: The constructor must be disabled. Yes, this is not a mistake; we must disallow the user from calling the constructor, and replace it with an alternative way of creating objects. You might have never seen this concept before, but it is certainly the only correct way of getting around our problem:
class Object
{
weak_ptr<Object>
parent;
list<shared_ptr<Object> > children;
protected:
virtual shared_ptr<Object> GetThis()
= 0;
public:
void AddChild(shared_ptr<Object>
obj)
{
children.push_back(obj);
obj->parent
= GetThis();
}
};
The only new item here so far is that we have defined a pure virtual function called GetThis, which makes the class return a shared_ptr to itself. The actual implementation has to be done in the concrete implementation. Object is just a base class that handles objects in general, but does not know anything about concrete objects in particular. The real trick is in the concrete implementation:
class Circle : public Object
{
weak_ptr<Object>
weak_this;
double x, y, r; //
fields
protected:
Circle(double _x,
double _y, double _r)
: x(_x), y(_y),
r(_r) { }
// It is a normal constructor, but it must
be protected
public:
static shared_ptr<Circle> Create(double x, double y, double
r)
{
shared_ptr<Circle>
obj(new
Circle(x, y, d);
obj->weak_this = obj;
return obj;
}
protected:
virtual shared_ptr<Object> GetThis()
{
shared_ptr<Object>
_this(weak_this);
return _this;
}
};
Huh, this is a mouthful! The member variables and the constructor are traditional; they just implement a very simple Circle class, inheriting from Object. It also has a weak_this member, which is going to hold a pointer to itself. It should be a weak_ptr.
The most important aspect is that the constructor must be protected. It can not be public, because it would undermine our intention that the weak_this member is always initialized right after the object is constructed. It can not be private either, because it would be impossible to inherit from it that way. Therefore there is no other choice but to make it protected.
We do not allow the user to call the constructor directly, so we need to provide an alternative way of creating objects. A global function would not work, because we need access to the protected constructor. Making the global function a friend of the class would work, but it is much nicer to declare it a public static member function. We call the replacement constructor “Create”, and it has the same input arguments as the constructor. Internally it’s quite simple but somewhat tricky at the same time. After using new to create a new instance, we store it in a shared_ptr, and assign that immediately to its own weak_this member. Finally, return the object. This does exactly what we need: It lets the new circle know who it is, by assigning itself to its weak_this member.
Finally, the GetThis() virtual function has to be implemented as well, but that is a trivial implementation. It just returns weak_this.
Here is how to use this class:
shared_ptr<Object> obj(Circle::Create(10.0,
15.0, 3.4));
The GetThis() function is protected, because it’s used by the class hierarchy only. It makes no sense to call obj->GetThis(), because it would return a copy of the obj variable, which you already have access to.
This implementation certainly has some overhead. First, it requires an additional “weak_this” member, which is almost always useless, except when the “this” pointer is needed. Second, it also requires a virtual member function, but that’s not a big deal, as it’s only called when the “this” pointer is needed.
The worst thing is that you have to modify your classes to disable the constructor and implement a relatively long infrastructure, which is a lot of typing. There is a way to automate this, using a Boost template class enable_shared_from_this and member function shared_from_this:
http://www.boost.org/libs/smart_ptr/sp_techniques.html#from_this
Unfortunately, you have to use multiple inheritance for the automated solution. In addition, you may not have enough flexibility that way. I prefer writing my own Create member, even if it means a blunt cut-and-paste.
If you have a fully standard compliant compiler, given an object hierarchy, you may create virtual functions, where each of them returns a pointer of its own type:
struct
Shape
{
virtual Shape*
Clone() const = 0;
};
struct
Rectangle : public Shape
{
virtual Rectangle*
Clone() const { return new Rectangle(*this); }
};
struct
Circle : public Shape
{
virtual Circle* Clone() const { return new Circle(*this); }
};
This is very convenient, because if you already have a Circle* pointer, you can clone it without using dynamic_cast. If all Clone functions were returning Shape*, you would often have to do an unnecessary dynamic_cast, as shown in the following example:
Circle* c =
new Circle;
Circle* c_clone = dynamic_cast<Circle*>(c->Clone());
Using the advantages of the covariant return types, the dynamic_cast is not necessary anymore, because Circle::Clone returns the type of Circle*. If you have to clone a polymorphic pointer of “Shape* p”, Clone returns Shape*. However, if you already know the specific type of the object, Clone returns the type of itself automatically, and sometimes you can take advantage of this.
Using shared_ptr or weak_ptr, you may not use covariant return types. Consider the following:
struct
Shape
{
virtual shared_ptr<Shape> Clone() const = 0;
};
struct
Circle : public Shape
{
virtual shared_ptr<Circle> Clone() const; // ERROR!
};
The error is due to the fact that the two Clone functions are not compatible. Circle::Clone can not override Shape::Clone, because their return types are unrelated. The two functions share the same name, the same argument list, yet their return types differ. The only way this would compile is if the two Clone functions used the same return type, or the return types were from the same hierarchy.
In our original example, Shape::Clone returns Shape*, and Circle::Clone returns Circle*, which is legal in modern C++, because the return types are related to each other. This is not true, however, in our second example. The problem is the same as what we had with the dynamic_cast before – shared_ptr<Circle> is not inherited from shared_ptr<Shape>, even though Circle itself is descended from Shape. It is not acceptable that two functions with the same name and the same argument list return completely unrelated types.
To solve this problem, we have to organize things a little differently:
struct
Circle : public Shape
{
virtual shared_ptr<Shape> Clone() const;
shared_ptr<Circle>
ConcreteClone() const;
};
Use ConcreteClone when you already know the type, and use Clone with polymorphic pointers. If you think it over, it is not that bad. This is the exact same solution as what programmers did before covariant return types were introduced in standard C++.