Reference counted pointers are a useful idiom for simplification of developing code. Using reference counts, it can be determined when an object is no longer needed and hence deleted without regard to knowledge of complex interrelations within a system. However, this simple idiom leeds to suprisingly complex code which inevitably leeds to errors in maintaining proper reference counts which can be a source for many future errors.
The use of C++ exceptions also may create resource leaks and possible logic errors which can be difficult to manage using try/catch blocks. The "Initialization IS Aquisition" idiom provides a handy way to eliminate errors from exceptions. Exception safety is very important for libraries as they are a common way to manage "exceptional" cases.
The name "smart pointer" is used to describe a pointer-like object that performs house-keeping. A smart pointer for "reference counted" objects is able to greatly simplify the programming tasks to maintain accurate reference counts. While there are a small number of smart pointer implementations, the design goals for this implementation is for performance, legacy support and support for class designs that are more appropriate for intrusive reference counting. (Intrusive reference counting is where the reference counting mechanism is provided within the class being managed).
There is no panacea however. The reference counting idiom creates a significant design issue, namely the circular reference. Designs which have circular references will leak and not perform as intended. Hence designs that require complex networks of pointers require non trivial design rules to eliminate the conditions that cause cyclic referencing. There are a number of tools that may be adopted to manage the effects of circular references and this library introduces the concept of the LeadTwin and AideTwin interfaces that may be used also to manage the circular reference class design issues.
A popular alternative library for managing shared pointers is boost::shared_ptr. Boost::shared_ptr is an alternative implementation and approaches the problem of circular references using the "weak_ptr" concept. The design goals of of boost::shared_ptr are different to that of the at::Ptr goals. boost::shared_ptr in particular does not manage legacy support without creating support classes and the weak_ptr support adds extra overhead that does not mesh with the Ptr* performance goal. (In most cases, at::PtrView or at::PtrDelegate is as efficient as passing raw pointers).
The set of co-operative "smart" pointers Ptr, PtrDelegate and PtrView simplify the task of maintaining reference counted pointers. The intent is to create an efficient mechanism yet easier for a programmer to avoid inadvertent errors.
PtrDelegate and PtrView are intended to be used to specify pointer transfer policy (function call or function return value) while Ptr is intended to be instantiated as an object member, automatic or global variable to maintain the reference to the object being managed. If used correctly, no explicit action to increment or decrement reference counts are required. In cases where PtrDelegate may not be appropriate to pass (perhaps as an STL container specialization), Ptr may be substituted in place of PtrDelegate.
It is important to note that PtrDelegate is intended to be initialized once and transferred into an Ptr or simply remain unreferenced. Once an PtrDelegate is assigned to an Ptr or another PtrDelegate, the internal managed pointer contained in the PtrDelegate object is no longer valid.
<th colspan=5>Smart Pointer Conversion Semantics</th> <th>LHS =</th> <th colspan=4 align=left>RHS</th> <th>Assign To Below</th> <th>Ptr</th> <th>PtrDelegate</th> <th>PtrView</th> <th>Regular Pointer</th>
** It is important to note that assignment of a raw pointer to a PtrDelegate or and Ptr does not increment the reference count while assignment to an PtrView followed by an assignment to a PtrDelegate or Ptr does increment the reference count. Regular pointers to reference counted objects should be rare and only available from a new or legacy interface.
Upon destruction of Ptr and PtrDelegate smart pointers, the reference count of the managed object is decremented. Note the omissoion of PtrView in the prior statement. This corresponds to the table above where assignment to PtrView performs no reference count increment.
Like any good craftsman, understanding the tools is the first imperative. The smart pointers described here are a very thin layer over regular pointers and if adopted correctly will eliminate virtually all leaks due to sloppy programming as well as provide exception safety.
Below are a set of guidelines for creating applications using smart pointers.
Below is a simple example of a class implemented with and without using smart pointers.
// A reference counted class (without smart pointers)
class Node : public PtrTarget_Basic
Node * left;
Node * right;
: left(0), right(0)
if ( left ) left->Release();
if ( right ) right->Release();
// i_node is passed without a reference count
void InsertLeft( Node * i_node )
Node * old_left = left;
// Note the AddRef before the Release !
// in complex systems a release may cause
// errors (for example releasing itself)
left = i_node;
if ( old_left ) old_left->Release();
// A reference counted class (using smart pointers)
class Node : public PtrTarget_Basic
Ptr<Node *> left;
Ptr<Node *> right;
void InsertLeft( PtrView<Node *> i_node )
left = i_node;
Note the simplification of the constructor and destructor between the 2 examples above. Also note the comment for InsertLeft in the first example where no such comment is needed for the InsertLeft with smart pointers.
As you can see above, the example is simple yet compulsive. More complex algorithms are also more complelling because the programming complexity for dealing with maintaining reference counting manually can become difficult to maintain. Experience suggests that smart pointers almost eliminate reference counting programming errors.
There are a number of other potential uses for Ptr. Just to touch on a few. The first one is obviously the result of a 0 reference count need not delete the object (deleting the object is simply the most common usage) and it may instead be used as a way to trigger some other event e.g. read lock/write lock sematics.
Generated for Austria by and MakeXS at Sun Oct 24 17:35:34 PDT 2004