Main Page   Modules   Namespace List   Class Hierarchy   Compound List   File List   Namespace Members   Compound Members   File Members  

Smart Pointer Support


Compounds

class  Ptr
class  PtrDelegate
class  PtrTraits
class  PtrView

Detailed Description

Introduction

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

AT_Life* Smart Pointer Basics

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>

<th>Ptr</th>
Increment Transfer Increment Assign **

<th>PtrDelegate</th>

Increment Transfer Increment Assign **

<th>PtrView</th>

Assign Assign Assign Assign **

Definitions of Semantics

** 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.

How to Use Smart Pointers

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.

  1. Never pass or return unmanaged reference counted pointers unless it is required by legacy interfaces. The pointers PtrView and PtrDelegate are "policy" pointers indicating the intention to pass along the obligation to the recipient to decrement the reference count. In some instances you may be required to pass an Ptr (e.g. if instantiating an STL std::list of pointers) which is not an issue.

  2. Whenever a reference counted object is created (the result of new or a factory request), place the pointer immediatly into an Ptr or PtrDelegate object. When managing a pointer in a legacy interface (e.g. Win32/COM) make sure to place the unmanaged raw pointer directly into a managed pointer of appropriate type (if obligation to release is passed, place into a Ptr or PtrDelegate or if no obligation is passed, place into an PtrView followed by assignment to an Ptr if you wish to maintain a reference to the object.

  3. Since PtrView is the least expensive (i.e. no increment of reference count is required) of the smart pointers, use this in preference to PtrDelegate where the situation permits.

  4. Be careful to note that once an PtrDelegate is assigned to another PtrDelegate or and Ptr, it's value is no longer valid and no further use of the smart pointer is possible. If the intention is to maintain a reference to the pointer, place the pointer in a Ptr and use the PtrDelegate smart pointer instead.

  5. Avoid cyclic dependantcies. (e.g. A->B B->A.) Cyclic references will not be managed correctly. Use a LeadTwin and AideTwin interface if appropriate or an indirection class to remove the cyclic dependantcy.

Example if Reference Counted 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;

    Node()
      : left(0), right(0)
    {}

    ~Node()
    {
        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)
        
        i_node->AddRef();
        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.

Unlimited Potential

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 doxygen and MakeXS at Sun Oct 24 17:35:34 PDT 2004