Using static_pointer_cast<T>(..) and dynamic_pointer_cast<T>(..)
This demonstrates how to use static_pointer_cast<T>(..) and dynamic_pointer_cast<T>(..). It is like the previous post which shows how to use const_pointer_cast<T>(..).
First the headers and some class declarations:
# include <boost/assert.hpp>
# include <boost/pointer_cast.hpp>
# include <boost/shared_ptr.hpp>
using boost::shared_ptr;
using boost::static_pointer_cast;
using boost::dynamic_pointer_cast;
class
base_type
{ public: virtual ~base_type( ) { } };
class
derived_type
: public base_type
{ };
class
derived_type_other
: public base_type
{ };
Notice base_type has a virtual destructor, which makes it a polymorphic class. A class has to have at least one virtual method if you want to use it with dynamic_pointer_cast<T>(..). Otherwise the compiler complains loudly.
Remember that, in general, declaring the destructors virtual is often not necessary if a class is being managed by shared_ptr<T>. If you always immediately attach a new target object to a shared_ptr< MOST_DERIVED_CLASS > after creation, then you don’t have to worry if shared_ptr< BASE_CLASS > later deletes that target object. The most-derived destructor is bound to the shared_ptr<T> during first attach and is passed along to subsequent shared and weak pointers.
Finally, here is the code showing static_pointer_cast<T>(..) and dynamic_pointer_cast<T>(..).
// Make a shared_ptr to a new object. shared_ptr< derived_type > sp_derived( new derived_type); // The shared_ptr knows how to upcast its // inner pointer. shared_ptr< base_type > sp_base( sp_derived); // You can static-downcast the inner pointer. shared_ptr< derived_type > sp_derived2( static_pointer_cast< derived_type >( sp_base)); // You can dynamic-downcast the inner pointer // as long as the inner type is polymorphic. shared_ptr< derived_type > sp_derived3( dynamic_pointer_cast< derived_type >( sp_base)); BOOST_ASSERT( sp_derived3); // You can try to dynamic-downcast to the wrong type. // You will end up with a null shared_ptr because // the inner dynamic-downcast returns a zero pointer. // This does not throw std::bad_cast because it casts // pointers and not refs. shared_ptr< derived_type_other > sp_derived_other( dynamic_pointer_cast< derived_type_other >( sp_base)); BOOST_ASSERT( ! sp_derived_other);
Like const_pointer_cast<T>(..), static_pointer_cast<T>(..) and dynamic_pointer_cast<T>(..) also work with intrusive_ptr<T>s and raw pointers. But they don’t work with weak_ptr<T>, at least not in Boost 1_37_0 (I suspect it’s an oversight). They also do not work with shared_array<T>, which makes sense since array “pointers” should always be to the most-derived type, and of course they don’t work with the single-ownership smart pointers that don’t support normal copy semantics.
There is also a reinterpret_pointer_cast<T>(..) function, but it only works with raw pointers. Reinterpret cast is usually only used at a very low abstraction level, while smart pointers are highly abstracted. If you think you need a reinterpret cast with a smart pointer you are probably doing something wrong.
Using const_pointer_cast<T>(..)
This is a quick demonstration of const_pointer_cast<T>(..).
# include <boost/shared_ptr.hpp>
using boost::shared_ptr;
using boost::const_pointer_cast;
struct test_type { };
// Make an object and give it to a shared_ptr.
shared_ptr< test_type >
sp_non_const(
new test_type);
// Copy the shared_ptr to another, except the
// new shared_ptr holds a pointer to a const.
// This copy can be an implicit cast.
shared_ptr< test_type const >
sp_const(
sp_non_const);
// Copy the shared_ptr to a const to another
// shared_ptr, one that holds a non-const
// pointer. This is not an implicit cast.
// This is what const_pointer_cast is for.
shared_ptr< test_type >
sp_non_const2(
const_pointer_cast< test_type >( sp_const));
And there you have it. const_pointer_cast<T>(..) is used to cast away the const (or volatile) buried inside a smart pointer. It works with shared_ptr<T>s, intrusive_ptr<T>s, and raw pointers. Without it you would have to resort to reinterpret_cast<T>(..) to cast away const buried inside a shared_ptr<T> unless it provided ->shared_from_this().
Managed memory and shared_ptr<T>s
In my last post I talk about how Boost implements shared_ptr<T>s with an intermediate object between the shared_ptr and the target. The intermediate object serves a few purposes.
It holds the reference count
Otherwise it would have to forgo the reference count (like a true garbage collector), or keep the reference count in a separate map/dictionary, or force the target object to make room for the reference count. The intermediate object lets shared_ptr<T> provide an efficient implementation that works with any target class.
It enables weak pointers
Weak pointers of class weak_ptr<T> point to the same intermediate object as shared_ptr<T>. There are ways to implement weak pointers without the intermediate object, but they are questionable. An intrusive implementation would require the target to provide a pointer from the target object to a linked list of weak pointers, which could then be cleared before the target was deleted. But this involves a lot of mutex-style locking and can be expensive. The Boost implementation is very clever about avoiding these kinds of locks, instead using interlocking increment/decrement.
Other solutions, involving delayed delete and property maps/dictionaries, are even less attractive.
It enables compaction
Some garbage collecting systems always use intermediate objects. The intermediate objects all together are usually called the object table. One of the big advantages is that when the garbage collector moves a target object it only has to update a single pointer, the one in the object table. The object table entries (the intermediate objects) do not move, but fragmentation isn’t much of a problem because they are small, kept in an array, and reused often.
It is easy to see how you might set up something similar in C++. It would provide compaction and reduce memory fragmentation even without full garbage collection. You’d allocate managed objects from a special heap, and when the heap got too fragmented you’d create another heap, copy the objects across, update all the pointers in the object table, and destroy the first heap. This is especially attractive if you are coding in a no-side-effects functional style.
(It wouldn’t work with Boost 1_37 shared_ptr<T>s though, since they not only store the target pointer in the intermediate object (where it is used for delete) but also in the shared_ptr<T> and weak_ptr<T> objects themselves. They do this because casting and aliasing require that the target pointer and the deleted pointer sometimes be different.)