Mirror of MacCompanion

macCompanion magazine






About Us








"Foreign" Macs



Link Lists

Mac 3D


Mac Jobs

MUG Shots




Think Different



Download this issue

According to Hoyle

According to Hoyle...

C++0x Part 4: Smart Pointers



January 2010

by Jonathan Hoyle


Three months ago, we began our series on the changes that will be coming to the C++ language.  We looked first at some of the bug fixes, general language improvements and features which made the language easier to use:

            • C++0x Part 1: What is It (and Does It Even Matter)?
            • C++0x Part 2: A Step Forward
            • C++0x Part 3: Making Coding Easier

We now turn at some of the more advanced features of the new C++.  This month, we look at smart pointers.



Smart Pointers

Smart Pointers are objects pointing to memory which are smart enough to know when to delete themselves, rather than rely upon the user to manage its deallocation.  Virtually all modern languages, such as Java and C#, manage memory in this fashion and thus avoid memory leakages and overstepping.  In these other languages, the procedure is (just as in C++) to simply call the new keyword to allocate dynamic memory; however (unlike in C++), the user typically ignores deallocation, assuming that the language's garbage collector will handle it.  C++ users have no automatic garbage collector, so he must make a delete call, lest he leaks memory.  Worse still, he must know when to call delete and when to call delete [] (as unhappiness can occur if the wrong one is called).




The C++98 Standard Library came a minimally "smart" pointer object, auto_ptr<>.  Unfortunately, auto_ptr<> has some severe limitations to it, one of the most severe being that it used an exclusive ownership model.  That is, the last auto_ptr<> receiving the assignment was the sole owner of the memory:

 auto_ptr<int>  ptr1(new int(0)); // ptr1 has exclusive access
 auto_ptr<int>  ptr2 = ptr1;      // ptr2 has exclusive access,
                                  //     ptr1 no longer does

This is counter-intuitive, as one does not expect the source object to change in such an assignment.

There are other severe drawbacks as well.  Those wishing to simply replace their pointers with auto_ptr<> will find quite a few syntactic differences:

There is no pointer arithmetic:

 ptr1++;           // Cannot increment an auto_ptr<>
 ptr2 = ptr + 1;   // Cannot add to the address of an auto_ptr<>
 ptr2 = ptr[7];    // Cannot array index into an auto_ptr<>

 It is limited by an explicit constructor:

 auto_ptr<int> ptr1 = new int(1);  // Constructor disallows this

 And, perhaps worst of all, there is no array capabilities.  If you attempted to create an array of integers with auto_ptr<>:

 auto_ptr<int> ptr1(new int[1024]); // Points to a block of int's

You would get direct access only to the first element.  Worse still, when auto_ptr<goes out of scope, it will call delete (not delete []), causing a memory leak.

In the end, it can be used for allocating a single object only, and owned exclusively by a single pointer object at any one time.  As "smart" pointers go, auto_ptr<is one of the dumbest.  For this reason, the C++ community has by and large rejected auto_ptr< and its use is now rather minimal.




C++0x Standard Library introduced a smarter pointer object, shared_ptr<>.  Its main difference over auto_ptr<is that it uses a shared ownership model using reference counting to determine when the memory should be deallocated.  For example:

    shared_ptr<int> ptr1;                // null smart ptr
       shared_ptr<int> ptr2(new int[1024]);
       ptr1 = ptr2;                      // both ptr1 & ptr2 own it
    // ptr2 destructed, only ptr1 owns it
    // memory not yet deallocated
 // ptr1 destructed, now delete is called on it

A shared_ptr<can be treated as a pointer, so it can be dereferenced like *ptr1 or call call methods upon the underlying data such as ptr1->foo().  The following are some constructors for shared_ptr<that make it useful to use:

 explicit shared_ptr<T>(T *ptr);   // Attaching to memory
 shared_ptr<T>(T *ptr, Fcn delFcn);// Attaching to memory and a
                                   //   user-defined deletion fcn

 shared_ptr<T>(shared_ptr<T> ptr); // Copy constructor
 shared_ptr<T>(auto_ptr<T> ptr);   // Converting from an auto_ptr<>

Note this last constructor converting the data from an auto_ptr<to a shared_ptr<>, making it easier for you to transition your previous code.  There are some additional utilities made available as well, such as a swap() routine and two cast routines: static_pointer_cast() and dynamic_pointer_cast().

Fortunately for Mac programmers, shared_ptr<is part of the std::tr1:: namespace, and thus is already available to Mac users using Xcode 2.x.




Although the shared_ptr<class has the same array limitations as auto_ptr<has, there is a shared_array<class that is designed specifically for array allocations.  This is annoying, as it propagates the need to distinguish between allocations of one object and allocations of more than one object.  C++ users of the past needed to know the difference so as to determine whether to call new/delete versus new[]/delete[]; C++ users of the future will still need to know this so as to determine whether to call shared_ptr<> or shared_array<>.

Sadly, not all of the limitations of auto_ptr<are solved with shared_ptr<> and shared_array<>, but it's a definite step forward.  (In a future macCompanion column, we will discuss the building of a smart pointer class which will solve more of these issues.)

Coming Up: 

C++0x Part 5: Rvalue References

C++0x Part 6: Final Thoughts


To see a list of all the According to Hoyle columns, visit: