Dual-mode weak pointers

Directory
Implementation details
Source download
Contact information
Author's home page

In C++, a plain pointer can be in one of three fundamentally different states: pointing to allocated memory, null, or indeterminate1. For example, a plain pointer has an indeterminate value if it was never initialized, or if its target object has been deleted. Generally, a weak pointer class reduces this model to just two states: a pointer to a constructed object, or null, reporting null if the target object has been deleted.

The weak pointers here use a slightly different approach, which models the three plain pointer states more closely. The major change is that the "indeterminate" state of a plain pointer is replaced by an "invalid" state with well-defined semantics. An attempt to access an uninitialized or invalidated weak pointer is considered an error and raises an exception.

Using this approach, it becomes possible to compile the weak pointers in two different modes, one of which maintains and checks validity information and another which simply wraps a plain pointer. Any code that functions correctly (i.e. doesn't throw) with checking enabled, also operates correctly with the checking disabled. This mirrors an assert macro which can be compiled out in non-debug builds.

1Actually, there is another possibility for a plain pointer: one-past-the-end of array. However, it's probably never useful to have a smart pointer to such an address, since (by definition) no object exists there.

Implementation

This form of weak pointer is simple to implement using an intrusive model, in which objects that can be pointed to by a weak pointer provide a separate "validity" flag. The target object's constructor allocates the flag and initializes it to true (i.e. "valid") and the destructor resets the flag to false. This means that the flag remains true throughout the lifetime of the target object, and false thereafter. In order to ensure that the validity flag itself doesn't disappear, any weak pointers that require the flag maintain a counted reference to it.

The diagram below shows what this looks like for two weak pointers referencing a single target object. On the left, the target object and each of the weak pointer objects have a counted reference (shown as dark arrows) to the validity flag. The lighter arrows represent plain pointers, which are not involved in the management of the target object's lifetime.

state with extant object   state after destruction
Pointers to a valid object   After target destruction

Destruction of the target object results in the situation shown on the right of the above diagram. The target object's destructor has cleared the validity flag, but the two remaining references prevent the flag from disappearing. The plain pointers now have indeterminate values2 (indicated by the question marks) and the weak pointers prevent any access to them.

Note that the weak pointers make no assumptions about how the target object was allocated, so it is even possible to use them for objects with automatic or static storage duration. This can be useful if you are retro-fitting the pointers to existing code, or for cases where you want to detect errors but allow various object management methods.

2Actually, if the destructor was invoked without deallocating the memory, the plain pointer values would still be usable for some operations. However, the weak pointers enter the "invalid" state as soon as the target object is destroyed.

Source download

weak_ptr.tar.gz (4kB). A sample implementation, including the weak pointer template, reference counted validity flag class and a base class to provide the intrusive support in client classes.

Usage details

[unfinished section]. Here is some slightly out of date information from an article I was working on:

Original article (text only)

Original article with diagrams and listings (16K zip file)

Original code (16K zip file) the original implementation using boost::shared_ptr instead of a purpose-made weak_ptr_flag class (slower).

Contact information

This page and the related sources are Copyright (c) 2002 by Raoul Gough and may be used and distributed free of charge. The author can usually be contacted via email to RaoulGough@yahoo.co.uk.