C++: Intrusive smart pointer which works for any type

Download the library.

Table of contents:

  1. Introduction
  2. RefPtr: An intrusive smart pointer which works with any type
  3. Usage
  4. License

Introduction

There are basically three types of smart pointers:

  1. Non-intrusive reference-counting smart pointer.
  2. Non-intrusive doubly-linked smart pointer.
  3. Intrusive reference-counting smart pointer.

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.

RefPtr: An intrusive smart pointer which works with any type

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:

Advantages:

Disadvantages:

Usage

Add #include "RefPtr.hh" to the source file where RefPtr is to be used.

To create a null RefPtr, simply create one without any constructor parameters:

    RefPtr<SomeType> ptr;

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:

    RefPtr<SomeClass> ptr(ptr.instantiate);

(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.)

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 operators):

RefPtr has support for custom allocators (mainly with FSBAllocator in mind). As mentioned earlier, using such an allocator is very easy and safe. For example:

    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 allocator with RefPtr:

(I know these requirements make RefPtr unusable for very exotic allocators, but the main idea is to support FSBAllocator, besides the default standard allocator.)

License

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.