
According to Hoyle...
C++0x Part 4: Smart Pointers
macCompanion
January 2010
by Jonathan Hoyle
jonhoyle@mac.com
http://www.jonhoyle.com
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 usual procedure to allocate dynamic memory is (just as
in C++) to simply call
the new keyword
followed by the type of the object you wish to create; however (unlike in C++), users
in many other languages typically ignore deallocation, assuming that the language's
garbage collector
will handle it. C++ users have no automatic garbage collector, so they must make
a delete call
on the object, lest
memory is leaked. Worse
still, they must know when to call
to delete and when to
call delete [] (for
arrays of ovjects being created), as unhappiness can occur if the wrong one is invoked).
auto_ptr<>
The
C++98 Standard Library
came with a minimally "smart" pointer object
called: auto_ptr<>. Unfortunately, auto_ptr<> has
some severe limitations to it, one of the most severe being that it uses an
exclusive ownership model. That
is to say, the
last auto_ptr<> receiving
the assignment is the sole owner of the memory holding it. Consider the
following example:
auto_ptr<int> ptr1(new int(0)); // ptr1 has exclusive access
auto_ptr<int> ptr2 = ptr1; // ptr2 now has exclusive access,
// ptr1 no longer does
This is counter-intuitive, as one does not expect the
source object (i.e.: the variable on the right side of the = sign) to change in
such an assignment.
There are some other severe drawbacks as well. Those
wishing to simply replace their pointers in code
with auto_ptr<> will
find quite a few syntactic differences.
First, there is no pointer arithmetic:
ptr1++; // Cannot increment an auto_ptr<>
ptr2 = ptr1 + 1; // Cannot add to the address of an auto_ptr<>
val = ptr2[7]; // Cannot array index into an auto_ptr<>
Next, it is limited by an
explicit constructor:
auto_ptr<int> ptr1 = new int(1); // Constructor disallows this syntax
Finally, and perhaps worst of all, there are no array
capabilities within this class. For example, let's say you allocate an array
of integers to be managed
by auto_ptr<> like
so:
auto_ptr<int> ptr1(new int[1024]); // ptr1 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, auto_ptr<> is
best used only for single object allocations, 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<> as
a useful solution, and its use is now rather minimal.
Visit
here for more
on auto_ptr<>.
shared_ptr<>
The
C++0x Standard Library
introduces a new smarter pointer object,
called: shared_ptr<>. Its
main difference
over auto_ptr<> is
that it uses a
shared ownership model, with
reference counting
used to determine when the memory it holds, is ready to be be deallocated. For example:
// function foo() using shared_ptr<>'s
void foo()
{
shared_ptr<TypeX> ptr1; // ptr1 is a null smart pointer
... // other code
// new scope
{
shared_ptr<TypeX> ptr2(new TypeX); // ptr2 points to a new object
ptr1 = ptr2; // ptr1 & ptr2 both own access
// to this same object
}
// ptr2 leaves scope, but does not deallocate the object,
// since it is still being pointed at by ptr1
...
}
// ptr1 leaves scope when foo() finishes, deallocating TypeX object
A shared_ptr<> can
be treated as a pointer, so it can be dereferenced
like *ptr1 or
call methods upon the underlying data type, such
as: ptr1->foo(). The
following are some constructors
for shared_ptr<> that
make it useful for C++ development:
explicit shared_ptr<T>(T *ptr); // Attaching to memory
shared_ptr<T>(T *ptr, Fcn delFcn); // Attaching to memory, with a
// user-defined deletion function
shared_ptr<T>(shared_ptr<T> ptr); // Copy constructor
shared_ptr<T>(auto_ptr<T> ptr); // Converting from auto_ptr<>
Note that this last constructor is converting the data from
an auto_ptr<> to
a shared_ptr<>,
making it easier for you to transition your previous code (had you been using the
older auto_ptr<> type). There
are some additional utilities made available as well, such as
a swap() routine
and two cast
functions: 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.
Visit
here for more
on shared_ptr<>.
shared_array<>
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 or new[]/delete[];
C++ users of the future will still need to know this so as to determine whether to
use 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 in the right direction. (In a future macCompanion column,
we will discuss the building of a custom smart pointer class which will solve more
of these issues.)
Visit
here for more
on shared_array<>.
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:
http://www.jonhoyle.com/maccompanion
http://www.maccompanion.com/macc/archives/January2010/Columns/AccordingtoHoyle.htm