
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 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).
auto_ptr<>
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.
shared_ptr<>
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:
main()
{
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.
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 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
http://www.maccompanion.com/macc/archives/January2010/Columns/AccordingtoHoyle.htm