Download the library.
Table of contents:
There are basically three types of smart pointers:
The first type of smart pointer has the advantage that it can be used with any type of allocated object. In other words, the smart pointer does not require anything from the object in question. However, it has two main problems compared to the intrusive type: Its size is that of two pointers (rather than one), and most importantly, it needs to dynamically allocate the reference counter (which is just an integer type) separately. Such an extra allocation not only slows down the creation and destruction of such smart pointers, but it also wastes memory (typically each allocation consumes 16 bytes of memory even though the reference counter itself typically only requires 4 bytes in 32-bit systems).
The second type of smart pointer is a clever variation: Rather than allocating a separate reference counting integer, what it does is that it doubly-links itself with all the other smart pointer instances which are pointing to the same object, effectively creating a doubly-linked list of smart pointers pointing to the same object. This way it has all the advantages of the first smart pointer type, but it doesn't need to allocate anything. Its disadvantage is that its size is that of three pointers.
The third type, the intrusive reference-counting smart pointer is the most efficient of all three, in all regards. "Intrusive" means that the reference counter is in the allocated object itself, rather than being allocated separately. This allows the intrusive smart pointer to have the size of a single regular pointer, as well as being almost as fast as such regular pointer for all operations (the only overhead in the smart pointer is that it increments and decrements the reference counter when it's copied, assigned and destroyed). Another advantage is that the reference counter itself does not require any additional memory overhead (besides its own size).
The problem with the intrusive smart pointer is that it requires the object to contain the reference counter. This means that it cannot be used with pre-existing objects which don't have it, nor with basic types.
The basic reason why traditional intrusive smart pointers can't be used with all possible types is that they leave the allocation of the object to the user, and simply expect the user to pass a pointer to this allocated object. Thus it's up to the user to ensure that the allocated object has the reference counter (for example by inheriting the instantiated class from a reference counter base class defined by the smart pointer library).
The smart pointer offered by this library,
RefPtr, takes a
different approach at the problem: It allocates the object itself, rather
than expecting the user to do it. This has both advantages and disadvantages:
RefPtrinstance is that of one single pointer, and the reference counter requires only 4 bytes of memory (8 bytes in 64-bit systems) because it's allocated in the same memory block as the object itself. (In practice the object and the reference counter are both members of the same struct.) The speed of
RefPtris that of a typical intrusive smart pointer.
RefPtrnot only works with incomplete (ie. forward-declared) types, but this support does not slow down copying nor assignment. (It is possible to make traditional intrusive smart pointers support incomplete types, but at the cost of copying and assignment of the smart pointer objects being slightly slower.)
new' and raw pointers involved in the user code. This allows using almost any type in a very similar way as you would use eg. STL containers. (In fact, one could think of
RefPtras being an STL container which can contain one single, reference-counted element.)
RefPtrhas been designed with the FSBAllocator in mind.
RefPtrdoesn't require the object to have an enabled copy constructor (although there are limitations to this; see below).
RefPtrallocates the object itself, it cannot be used in situations where, for example, some function returns a raw pointer to a dynamically-allocated object, and then the calling code would assign it to a smart pointer. In this case some other more generic smart pointer has to be used.
RefPtrcannot be used as a base class type pointer which points to derived class type objects. (On the other hand, this is the exact same limitation STL containers have, so it still can be very useful.)
RefPtrdoesn't require the object to be copy-constructible, as it passes all constructor arguments to the object constructor. However, this comes with a limitation: Currently only up to 6 constructor arguments can be passed like this. If more than this need to be passed, then it can be done by either constructing the
RefPtrwith a constructed object (which requires a working copy constructor), or adding an additional constructor to the
RefPtrsource code. (This problem will be fixed with the next C++ standard, which will have support for variadic templates.)
RefPtris not thread-safe.
#include "RefPtr.hh" to the source file where
RefPtr is to be used.
To create a null
RefPtr, simply create one without any
To instantiate an object and initialize it, give the initialization value or values as contructor parameter, for example:
RefPtr<double> ptr1(5.2); RefPtr<SomeClass> ptr2(1, 2, 3); // SomeClass constructor takes 3 parameters
To instatiate an object which takes no constructor parameters at all, it can be done like this:
(This little trickery is necessary for
RefPtr to distinguish
between whether the user wants to create a null pointer or instantiate a
type which takes no constructor parameters. Although it might look odd,
it's completely safe, as '
instantiate' is simply a value of
an enumerated type inside
RefPtr supports incomplete types. The only requirement is
that the type must be complete in the context where it's constructed with
parameters (the default constructor does not require for the type to be
complete). Copying, assigning, (in)equality comparison and destroying the
RefPtr instance do not require for the type to be complete
in that context.
RefPtr has the following public functions (besides the
constructors, copy assignment operator, destructor and equality/inequality
operator->()(for access like "
operator*()(for access like "
get(): Get the raw pointer to the object (use with care)
isShared(): Whether there is more than one
RefPtrpointing to the object
operator bool(): To check if it's a null pointer (eg. "
RefPtr has support for custom allocators (mainly with
FSBAllocator in mind). As
mentioned earlier, using such an allocator is very easy and safe. For
typedef RefPtr<SomeClass, FSBAllocator<SomeClass> > Ptr_t; Ptr_t ptr1(1, 2, 3), ptr2(4);
(As you can see, the only place where the allocator needs to be mentioned
is as the second
RefPtr template parameter. Everything else is
handled automatically, and thus the
RefPtr type can be used
exactly in the same way as with the default allocator. This makes it very
similar in usage to the STL containers.)
There are some limitations (imposed by practicality) on using a custom
(I know these requirements make
RefPtr unusable for very
exotic allocators, but the main idea is to support FSBAllocator, besides
the default standard allocator.)
This library is released under the MIT license.
Copyright (c) 2008 Juha Nieminen
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.