In my last post, I presented the
usefulness and necessity of RAII. In this post I want to walk you through the
implementation of the second simplest imaginable smart pointer:
scoped_ptr class template should support the
-> operators, and
delete the pointee in its destructor (i.e. when a
scoped_ptr object goes out
of scope). This is what
std::unique_ptr also does, but we will
see that our
scoped_ptr lacks some of
Reimplementing raw pointer functionality
Let’s start with the easy part. Since we want to be able to use
with any pointee type, we make it a template. Also, the only thing we will need
to store in the
scoped_ptr class is the actual pointer:
If we want to be able to use this class at all, we will first need constructors:
This fills two roles: First it acts as the default constructor that initializes
m_ptr member with
nullptr, thanks to the default argument
p. Second, it can be passed a raw pointer to initialize the held pointer to
that. I added the
explicit because implicit conversions from a raw pointer to
scoped_ptr could easily lead to an accidental
delete on an object that is
still needed (yes, right now there is no
delete in sight, but it will follow).
Next, the pointer operators
The arrow operator
-> is a bit special: It looks like a binary operator when
dialog->show()) but is overloaded with only one argument (the implicit
this pointer here). The reason is that the task of such an overload is just to
return a raw pointer on which the “real”
-> is applied. There is no other way
possible: C++ offers no way to select members by name at compile time.
The dereferencing operator
* hopefully does what you would have thought: It
returns a mutable reference to the
scoped_ptr’s pointee. Returning by value
would not make sense as you could then never change the pointed-to object.
These functions are
const because they do not change the pointer, they only
allow changing the pointee. Like
int* const p (in contrast to
int const* p
const int* p) allows
*p = 42,
scoped_ptr<int> const p does too (in
scoped_ptr<int const> ==
Now the class is ready to be used like a raw pointer, with the additional
advantage of being automatically initialized to
nullptr if no other value is
Well, there is one problem left: The memory is obviously leaked and we cannot
The actual RAII functionality
Of course, avoiding leaks was the original intention of even creating this class, so let’s fix this:
Fine! That fixes the above code, which is now leak-free, without a single
delete.1 The core of our new RAII class is now complete.
scoped_ptr now has a problem: Remember the Rule of Three?
If a class has a user-defined destructor, copy constructor or assignment operator, it also needs the other two.
We have a user-defined destructor but still use the compiler-generated copy
constructor and assignment operator, which is bad, since they will just copy
m_ptr leading to multiple
deletes of the same pointer value:
This is an important point: A RAII class is subject to the Rule of Three (or Five in C++11) and thus needs to take care not only of freeing a resource, but also of what happens when you copy(-assign) it. Scott Meyers outlines the three basic options in Item 15 of Effective C++ (3rd edition):
- Copy the resource (deep copy).
- Share the resource, with the copied RAII class; when the last copy is destroyed, the resource is also destroyed.
- Disallow copying.
Option 1 is impossible for smart pointers in the general case, since we cannot copy a polymorphic class via a pointer to a base. It’s also usually not what you want from a pointer.
Option 2 is actually quite useful and comes very close to garbage collection
(minus collection of cyclic references, i.e. objects referencing each other).
The standard library implementation of a smart pointer that uses this option is
std::shared_ptr and I recommend you to take a look at it (but as
it comes with a performance and memory overhead, use it only when a
std::unique_ptr is not enough!).
Option 3 is what we are gonna do:
= delete declaration here has nothing to do with the ordinary
delete ptr; It is a C++11 feature that instructs the compiler not to generate
these normally compiler-generated functions, so that any uses of them will lead
to a compile-time error. Now the code above will not make the
program crash (or whatever else the undefined behavior might cause) but instead
just fail to compile with a (hopefully) nice error message that allows us to
quickly find and fix the problem.
In practice it is often useful or even necessary to access the raw pointer
wrapped by the
scoped_ptr, so we will provide a member function
returns it.2 Although we disallow assigning
scoped_ptrs to each other, it
is still useful to reassign the underlying raw pointer (deleting the old one).
For this we will provide a
reset function. The final class definition is then:
An implementation of
scoped_ptr that is a superset of the above is available
in Boost.SmartPtr. It additionally provides conversion to
that checks if the pointer is
nullptr and a
std::unique_ptr is, again, a superset of
You generally always want to use this instead of
boost::scoped_ptr if you have
a C++11 compiler. It provides pointer comparison operators (both with other
unique_ptrs and raw pointers), support for customizing how the pointer should
be deleted, a
release member function that returns the raw pointer and assigns
nullptr without deleting it, and, most importantly, it can
be moved. Moveability means that although you cannot copy a
unique_ptr you can
still return it from a function or assign it to another
unique_ptr by moving
After this snippet,
pJ contains the pointer to the
int; it was moved from
std::move is completely new to you, you might be interested in reading
about rvalue references and move semantics (e.g. in this Stackoverflow
Since C++14 there is also the
std::make_unique function: It
new but directly returns a
unique_ptr instead of a raw pointer.
Besides the synergy with
auto type deduction, it brings C++ closer to
completely eradicating not only
delete but also
new from normal code. See
this article by Herb Sutter for more.
The implementation of RAII classes is pretty much the only place where
deleteis acceptable in good C++ code. ↩
In fact, such a function that returns the underlying resource handle from a RAII wrapper class is useful not only for smart pointers but for all kinds of resources. For example the application framework Qt provides a
winIdfunction for the
QWidgetclass that returns the native OS handle of the widget to allow interoperation with platform-specific functions. ↩