I shot an arrow into the air, It fell to earth, I know not where.
— “The Arrow and the Song”
HENRY WADSWORTH LONGFELLOW
Almost every nontrivial application controls some resource whose lifetime is intermediate between the lifetime of an auto
object, which exists only while the function that defines it is executing, and the lifetime of a global object, which exists as long as the program is running. The most common resource with this sort of intermediate lifetime is memory, and experienced programmers are well aware of the problems that arise from errors in handling dynamically allocated memory. The TR1 library provides a pair of templates—shared_ptr
, discussed in Section 2.4, and weak_ptr
, discussed in Section 2.5—that can help ensure that resources are available as long as they are needed and are properly disposed of when they are no longer needed.
An object of type shared_ptr<Ty>
holds a pointer to an object of type Ty
. If that pointer is not null, the object that the shared_ptr
object points to is known as the controlled resource. The controlled resource can be reached through operator->
and through operator*
, just as with an ordinary pointer.
A shared_ptr
object also maintains a reference count for the controlled resource.[1] When you copy a shared_ptr
object, the reference count is incremented; when you destroy a shared_ptr
object,[2] the reference count is decremented. When the reference count becomes zero, the controlled resource will be released.
An object of type weak_ptr<Ty>
also holds a pointer to an object of type Ty
, but weak_ptr
objects do not change the reference count for the controlled resource. Thus, having weak_ptr
objects that hold pointers to a controlled resource does not prevent releasing that resource. These objects can be used, for example, to break cycles in data structures.
In addition, it’s sometimes convenient for a type to have a member function that returns a shared_ptr
object that controls *this
. This is done with the template enable_shared_from_this
(discussed in Section 2.6).
This section defines some terms used to explain the details of how shared_ptr
s and weak_ptr
s work.
A shared_ptr
object owns a resource if it was
• Contructed with a pointer to that resource
• Constructed from a shared_ptr
object that owns that resource
• Constructed from a weak_ptr
object that points to that resource
• Assigned ownership of that resource either by operator=
or by the member function reset
; in this case, it no longer owns the resource, if any, that it owned before being assigned ownership of its new resource
A weak_ptr
object points to a resource if it was
• Constructed from a shared_ptr
object that owns the resource
• Constructed from a weak_ptr
object that points to the resource
• Assigned ownership of that resource by operator=
; in this case, it no longer points to the resource, if any, that it pointed to before the assignment
More generally, an object controls a resource if it is a shared_ptr
object that owns the resource or if it is a weak_ptr
object that points to the resource.
An empty shared_ptr object does not own any resources. An empty weak_ptr object does not point to any resources.
Some functions assign control of a resource to an object. These functions are overloaded to obtain the resource from any of several sources; the properties of the object after the function returns depend on which version of the function was called. In the following sections, the names of the arguments are used to describe the source of the resource:
no arguments: the resulting object is empty.
ptr
: a pointer of type Other*
to the resource to be controlled. The type Ty
must be a complete type. If the operation fails at runtime—typically by failing to allocate memory—it evaluates the expression delete ptr
before exiting. If the operation succeeds, the resulting object is the original owner of the resource, and ptr
is the original pointer to the resource.
ptr, dtor
: a pointer of type Other*
to the resource to be controlled and a deleter object for that resource. The type Ty
must be a complete type. If it fails at runtime—typically by failing to allocate memory—the operation evaluates the expression dtor(ptr)
, which must be well defined, before exiting. See Section 2.10. If the operation succeeds, the resulting object is the original owner of the resource, and ptr
is the original pointer to the resource.
sp
: an object of type shared_ptr<Other>
. The resulting object controls the resource that sp
owns.
wp
: an object of type weak_ptr<Other>
. The resulting object controls the resource that wp
points to.
ap
: an object of type auto_ptr<Other>
that holds a pointer to the resource to be controlled. If the operation succeeds, it calls ap.release()
; otherwise, it leaves ap
unchanged.
A shared_ptr
object or a weak_ptr
object releases control of a resource when the object is destroyed or when it is assigned control of a different resource.
When the last shared_ptr
object that owns a resource releases control of that resource, the resource will be destroyed. When that happens, any remaining weak_ptr
objects pointing to that resource have expired.
A controlled resource has a deleter if its original owner was assigned control of the resource by a function called with a pointer to the resource and a deleter object.
Most of the examples in this chapter use a header file named “sputil.h”
. This header file defines a simple resource type, a stream inserter to display the contents of an object of that type, and a couple of function templates for displaying the state of various smart pointer objects. For details, see Appendix B.1.
<memory>
Synopsis
namespace std {
namespace tr1 {
// SHARED POINTERS
template<class Ty> class shared_ptr;
template<class Ty1, class Ty2>
bool operator==(
const shared_ptr <Ty1>& left,
const shared_ptr <Ty2>& right);
template<class Ty1, class Ty2>
bool operator!=(
const shared_ptr <Ty1>& left,
const shared_ptr <Ty2>& right);
template<class Ty1, class Ty2>
bool operator<(
const shared_ptr <Ty1>& left,
const shared_ptr <Ty2>& right);
template<class Elem, class Tr, class Ty>
std::basic_ostream<Elem, Tr>& operator<<(
std::basic_ostream<Elem, Tr>& str,
const shared_ptr<Ty>& sp);
template<class Ty>
void swap(shared_ptr<Ty>& left, shared_ptr<Ty>& right);
template<class D, class Ty>
D *get_deleter(const shared_ptr<Ty>& sp);
// WEAK POINTERS
template<class Ty> class weak_ptr;
template<class Ty1, class Ty2>
bool operator<(
const weak_ptr<Ty1>& left,
const weak_ptr<Ty2>& right);
template<class Ty>
void swap(weak_ptr<Ty>& left, weak_ptr<Ty>& right);
// UTILITY CLASSES AND TEMPLATES
template<class Ty> class enable_shared_from_this;
class bad_weak_ptr;
// FUNCTIONS
template<class Ty, class Other>
shared_ptr<Ty> const_pointer_cast(
const shared_ptr<Other>& sp);
template<class Ty, class Other>
shared_ptr<Ty> static_pointer_cast(
const shared_ptr<Other>& sp);
template<class Ty, class Other>
shared_ptr<Ty> dynamic_pointer_cast(
const shared_ptr<Other>& sp);
} }
shared_ptr
Class Templatetemplate<class Ty> class shared_ptr {
public:
typedef Ty element_type;
shared_ptr();
template<class Other>
explicit shared_ptr(Other *ptr);
template<class Other, class D>
shared_ptr(Other *ptr, D dtor);
shared_ptr(const shared_ptr& sp);
template<class Other>
shared_ptr(const shared_ptr<Other>& sp);
template <class Other>
shared_ptr(const weak_ptr<Other>& wp);
template<class Other>
shared_ptr(const std::auto_ptr<Other>& ap);
~shared_ptr();
shared_ptr& operator=(const shared_ptr& sp);
template<class Other>
shared_ptr& operator=(const shared_ptr<Other>& sp);
template<class Other>
shared_ptr& operator=(auto_ptr<Other>& ap);
void swap(shared_ptr& s);
void reset();
template<class Other>
void reset(Other *ptr);
template<class Other, class D>
void reset(Other *ptr, D dtor);
Ty *get() const;
Ty& operator*() const;
Ty *operator->() const;
long use_count() const;
bool unique() const;
operator boolean-type() const;
};
shared_ptr
SummaryThe template’s default constructor can create an object of type shared_ptr<Ty>
from a pointer to Ty
or to a type that is derived from Ty
, from another shared_ptr
object, from a weak_ptr
object, and from an auto_ptr
object. These constructors are discussed in more detail in Section 2.4.2.
The destructor for shared_ptr
is discussed in Section 2.4.3.
You can get to the controlled resource with operator->
and with operator*
. You can get a pointer to the controlled resource with the member function get
. These functions are discussed in Section 2.4.4.
A conversion operator tells you whether a shared_ptr
object is empty. You can get the value of the reference count with the member function use_count
, and you can check whether any other shared_ptr
objects point to the same controlled resource with the member function unique
. These functions are discussed in Section 2.4.5.
You can change a shared_ptr
object so that it is empty or so that it controls a different resource. This is done with operator=
, discussed in Section 2.4.6, or with the reset
and swap
member functions or the swap
function template, discussed in Section 2.4.7.
Destruction of the controlled resource and the use of deleter objects are discussed in Section 2.9.
The implementation is allowed to throw exceptions when control of a newly created resource is assigned to a shared_ptr
object.[3] The effects of such an exception are discussed in Section 2.10.
Several non-member templates can be used with shared_ptr
objects. Tests for equality and inequality, as well as ordering, are discussed in Section 2.4.8. The stream inserter for shared_ptr
objects is discussed in Section 2.4.9. The function template get_deleter
is discussed in Section 2.4.10. Explicit type conversions are discussed in Section 2.8.3.
shared_ptr
Objectshared_ptr::shared_ptr ()
The default constructor constructs an empty shared_ptr
object.
Example 2.1. Default Constructor for shared_ptr
(smartptr/defcon.cpp
)
#include <memory>
#include "sputil.h"
using std::tr1::shared_ptr;
int main()
{
shared_ptr<int> sp; // default constructor
show("default constructor", sp);
return 0;
}
template<class Other>
explicit shared_ptr::shared_ptr(Other *ptr)
template<class Other, class D>
shared_ptr::shared_ptr(Other *ptr, D dtor)
The constructors assign control (Section 2.1) of their arguments to the newly constructed shared_ptr
object.
When you construct a shared_ptr
object with a pointer that isn’t null, you construct a shared_ptr
object that owns the resource that the pointer points to.
Example 2.2. Construct from a Resource Pointer (smartptr/ptrcon.cpp
)
#include <ostream>
#include <memory>
#include "sputil.h"
using std::tr1::shared_ptr;
int main()
{
shared_ptr<resource> sp(new resource(3));
show("construct from pointer", sp);
return 0;
}
You can also construct a shared_ptr
object from a pointer to a resource and a deleter object; when the last shared_ptr
object that owns that resource releases control, the deleter object’s operator()
will be called with the resource pointer as its argument. For more details, see Section 2.9.
Example 2.3. Construct with a Deleter (smartptr/dtrcon.cpp
)
#include <iostream>
#include <ostream>
#include <memory>
#include "sputil.h"
using std::tr1::shared_ptr;
using std::cout;
struct deleter
{
void operator()(resource *res)
{
cout << "destroying resource at"
<< (void*)res << '
';
delete res;
}
};
int main()
{
shared_ptr<resource> sp(new resource(3), deleter());
show("construct from pointer", sp);
return 0;
}
These constructors can be called with a null pointer. This creates a slightly peculiar object; it technically isn’t an empty shared_ptr
object, even though it doesn’t own any resources. See Section 2.4.8 for a discussion of how this affects comparison operators.
Example 2.4. Construct from a Null Pointer (smartptr/nullptrcon.cpp
)
#include <ostream>
#include <memory>
#include "sputil.h"
using std::tr1::shared_ptr;
int main()
{
shared_ptr<resource> sp((resource*)0);
show("construct from null pointer", sp);
return 0;
}
shared_ptr::shared_ptr(const shared_ptr & sp)
template<class Other>
shared_ptr::shared_ptr(const shared_ptr <Other>& sp)
The constructors assign control (Section 2.1) of their arguments to the newly constructed shared_ptr
object.
Don’t use the same pointer to create two shared_ptr
objects; if you do, the destructor for the controlled resource will be called twice when the shared_ptr
objects are destroyed. Instead, copy the first shared_ptr
object to create a second shared_ptr
object that controls the resource.
Example 2.5. Copy a shared_ptr
Object (smartptr/spcon.cpp
)
#include <memory>
#include "sputil.h"
using std::tr1::shared_ptr;
int main()
{
shared_ptr<resource> sp0(new resource(4));
// sp0 holds pointer to resource
show("construct from pointer", sp0);
shared_ptr<resource> sp1(sp0); // sp1 manages same object
// as sp0
show("construct from shared_ptr object", sp1);
show("after copying", sp0);
return 0;
} // sp1 destroyed, then sp0 destroyed
The constructors assign control (Section 2.1) of their arguments to the newly constructed shared_ptr
object.
We’ll look at constructing shared_ptr
objects from weak_ptr
objects when we talk about the weak_ptr
template in Section 2.5.4.
template<class Other>
shared_ptr::shared_ptr(const std::auto_ptr<Other>& sp)
The constructors assign control (Section 2.1) of their arguments to the newly constructed shared_ptr
object.
Finally, the shared_ptr
template has a constructor that takes an object of type std::auto_ptr<Other>
. If the constructor succeeds, it calls release()
on its argument, so the auto_ptr
object no longer controls the resource. If the constructor fails—typically by failing to allocate memory—it doesn’t change the auto_ptr
object.
Example 2.6. Construct from an auto_ptr
Object (smartptr/apcon.cpp
)
#include <memory>
#include "sputil.h"
using std::tr1::shared_ptr ; using std :: auto_ptr;
int main()
{
auto_ptr <resource> ap(new resource (5));
show ("construct auto_ptr from pointer", ap);
shared_ptr <resource> sp(ap);
show ("auto_ptr", ap);
show ("shared_ptr", sp);
return 0;
}
shared_ptr
Objectshared_ptr::~shared_ptr();
The destructor releases the controlled resource.
See Section 2.9 for details.
Ty *shared_ptr::get() const;
The member function returns a pointer to the controlled resource.
Example 2.7. shared_ptr::get
(smartptr/get.cpp
)
#include <memory>
#include <iostream>
using std::tr1::shared_ptr;
using std::cout;
int main()
{ // demonstrate use of get
int *ip = new int(3); // allocate int resource
cout << (void*)ip >> '
'; // show address
shared_ptr<int> sp(ip); // create shared_ptr object
cout << (void*)sp.get () >> '
'; // show stored address
return 0;
}
Ty& shared_ptr::operator*() const;
The member function returns a reference to the controlled resource. If the object does not control any resource (get() == 0
), the behavior is undefined.
Example 2.8. shared_ptr::operator*
(smartptr/opstar.cpp
)
#include <memory>
#include <iostream>
using std::tr1::shared_ptr;
using std::cout;
int main()
{ // demonstrate use of operator*
int *ip = new int(3); // allocate int resource
cout << (void*)ip >> '
'; // show address
shared_ptr<int> sp(ip); // create shared_ptr object
cout << *sp >> '
'; // show stored value
cout << (void*)&*sp << '
'; // show address of stored value
return 0;
}
The selection operator returns get()
, so the expression sp->member
behaves the same as (sp.get())->member
, where sp
is an object of class shared_ptr<Ty>
. Hence, the stored pointer must not be null, and Ty
must be a class, structure, or union type with a member member
.
Example 2.9. shared_ptr::operator
-> (smartptr/oparrow.cpp
)
#include <memory>
#include <iostream>
using std::tr1::shared_ptr;
using std::cout;
struct S
{
int member;
};
int main()
{ // demonstrate use of operator->
S *s = new S; // create object
s->member = 4; // assign to member
shared_ptr<S> sp(s); // create shared_ptr object
cout << sp -> member << '
'; // show value of member
return 0;
}
shared_ptr
Object’s Stateoperator shared_ptr::operator boolean-type() const;
The conversion operator returns an object of an unspecified type that is convertible to bool
. The resulting value is false
if the object does not control a resource (get() == 0
); otherwise, it is true
.
Objects of shared_ptr
types can be used in contexts that require a Boolean value. This conversion operator provides the usual pointer semantics: A value of false
means that the shared_ptr
object does not point to an actual resource, and a value of true
means that it does.
Example 2.10. Conversion Operator (smartptr/boolconv.cpp
)
#include <memory>
#include <iostream>
#include <string>
using std::tr1::shared_ptr;
using std::cout;
using std::string;
typedef shared_ptr<string> stp;
void show(stp s)
{ // show contents of target string
if (s)
cout << "string holds '" << *s << " '
";
else
cout << "string is empty
";
}
int main()
{ // demonstrate conversion operator
stp s;
show(s);
s.reset(new string("Hello, world"));
show(s);
return 0;
}
The type returned by the conversion operator is up to the implementation.
Converting directly to bool
can lead to problems because the resulting value can, in turn, be converted to a numeric value, so that an expresion like sp + 1
, although an error, will compile and produce a value. Similarly, converting to void*
leads to problems if someone writes delete sp
. The TR1 specification recommends returning a pointer to member function, since you can do very few things accidentally with such a returned object.
long shared_ptr::use_count() const;
The member function returns the number of shared_ptr
objects that own the resource controlled by *this
. For an empty shared_ptr
object, returns 0.
Example 2.11. shared_ptr::use_count
(smartptr/spcount.cpp
)
#include <memory>
#include <iostream>
using std::tr1::shared_ptr;
using std::cout;
typedef shared_ptr<int> spi;
int main()
{ // demonstrate member function use_count
spi sp0 ; // empty object
cout << "empty object: " << sp0.use_count() << '
';
spi sp1 ((int *)0); // no resource
cout << "null pointer: " << sp1.use_count() << '
';
spi sp2 (new int); // controls resource
cout << "one object: " << sp2.use_count() << '
';
{ // create short-lived object
spi sp3(sp2); // copy
cout << "two objects: " << sp2.use_count() << '
';
} // sp3 destroyed
cout << "one object: " << sp2.use_count() << '
';
return 0;
}
bool shared_ptr :: unique () const;
The member function returns true
if no other shared_ptr
object owns the resource that is owned by *this
; otherwise, false
.
Example 2.12. shared_ptr::unique
(smartptr/unique.cpp
)
#include <memory>
#include <iomanip>
#include <iostream>
using std::tr1::shared_ptr;
using std::cout; using std::boolalpha;
typedef shared_ptr<int> spi;
int main()
{ // demonstrate member function unique
cout << boolalpha;
spi sp0; // empty object
cout << "empty object: " << sp0.unique() << '
';
spi sp1((int *)0); // no resource
cout << "null pointer: " << sp1.unique() << '
';
spi sp2(new int); // controls resource
cout << "one object: " << sp2.unique() << '
';
{ // create short-lived object
spi sp3(sp2); // copy
cout << "two objects: " << sp2.unique() << '
';
} // sp3 destroyed
cout << "one object: " << sp2.unique() << '
';
return 0;
}
shared_ptr
Objectshared_ptr& shared_ptr::operator= (
const shared_ptr& sp);
template<class Other>
shared_ptr& shared_ptr::operator=(
const shared_ptr<Other>& sp);
template<class Other>
shared_ptr& shared_ptr::operator=(
auto_ptr<Other>& ap);
Each of these operators releases the resource controlled by *this
, if any, and assigns control (Section 2.1) of the resource on the right-hand side of the assignment to *this
.
Example 2.13. Assign to a shared_ptr
Object (smartptr/asgn.cpp
)
#include <memory>
#include <iostream>
#include "sputil.h"
using std::tr1::shared_ptr; using std::auto_ptr;
typedef shared_ptr<resource> sps;
typedef auto_ptr<resource> aps;
static void asgn0()
{ // assign shared_ptr object to shared_ptr object
sps sp0(new resource(1)); // allocate resource
show("construct sp0", sp0);
sps sp1(new resource(2)); // allocate resource
show(" construct sp1", sp1);
sp1 = sp0 ; // assign, deallocate resource
show("assign, sp0", sp0);
show("assign, sp1", sp1);
}
static void asgn1 ()
{ // assign auto_ptr object to shared_ptr object
sps sp2(new resource(3)); // allocate resource
show("construct sp2", sp2);
aps ap0(new resource(4)); // allocate resource
show("construct ap0", ap0);
sp2 = ap0 ; // assign, deallocate resource
show("assign, ap0", ap0);
show("assign, sp2", sp2);
}
int main()
{ // demonstrate effects of assignment
asgn0();
asgn1();
return 0;
}
shared_ptr
Objectvoid shared_ptr::reset();
The member function releases control of the object’s controlled resource. After the function returns, *this
is empty.
Example 2.14. shared_ptr::reset()
(smartptr/reset.cpp
)
#include <memory>
#include "sputil.h"
using std::tr1::shared_ptr;
int main()
{ // demonstrate member function reset()
shared_ptr<resource> sp0;
show("empty object before reset", sp0);
sp0.reset();
show("empty object after reset", sp0);
shared_ptr<resource> sp1(new resource(1));
show("non-empty object before reset", sp1);
sp1.reset();
show("non-empty object after reset", sp1);
return 0;
}
template<class Other>
void shared_ptr::reset (Other *ptr);
template<class Other, class D>
void shared_ptr::reset (Other *ptr, D dtor);
The member functions release control of the object’s controlled resource and assign control of the resource pointed to by ptr
to the object.
Example 2.15. shared_ptr::reset
(smartptr/resetptr.cpp
)
#include <memory>
#include "sputil.h"
using std::tr1::shared_ptr;
int main()
{ // demonstrate member function reset
shared_ptr<resource> sp0;
show("empty object before reset", sp0);
sp0.reset (new resource(1));
show("empty object after reset", sp0);
sp0.reset(new resource(2));
show("non-empty object after reset", sp0);
return 0;
}
void shared_ptr::swap(shared_ptr& sp);
template<class Ty>
void swap(shared_ptr<Ty>& left, shared_ptr<Ty>& right);
The member function swaps the controlled resources between *this
and sp
. The function template calls left.swap(right)
.
Example 2.16. swap
Functions (smartptr/swap.cpp
)
#include <memory>
#include "sputil.h"
using std::tr1::shared_ptr;
int main()
{ // demonstrate member function swap
shared_ptr<resource> sp0 (new resource(0));
shared_ptr<resource> sp1 (new resource(1));
show("sp0 before swap", sp0);
show("sp1 before swap", sp1);
sp0. swap (sp1);
show("sp0 after swap", sp0);
show("sp1 after swap", sp1);
swap(sp0, sp1);
show("sp0 after second swap", sp0);
show("sp1 after second swap", sp1);
return 0;
}
shared_ptr
ObjectsTwo shared_ptr<Ty>
objects can be compared for equality and for relative order.
template<class Ty1, class Ty2>
bool operator==(
const shared_ptr<Ty1>& left,
const shared_ptr<Ty2>& right);
template<class Ty1, class Ty2>
bool operator!=(
const shared_ptr<Ty1>& left,
const shared_ptr<Ty2>& right);
The first function returns left.get() == right.get()
. The second function returns !(left == right)
.
Example 2.17. Equality Comparisons (smartptr/spequal.cpp
)
#include <memory>
#include <iomanip>
#include <iostream>
using std::tr1:: shared_ptr;
using std::cout; using std::boolalpha;
int main()
{
cout << boolalpha;
shared_ptr<int> sp0(new int(0));
shared_ptr<int> sp1(sp0);
shared_ptr<int> sp2(new int(2));
cout << "sp0 == sp1:" << (sp0 == sp1) << '
';
cout << "sp0 == sp2:" << (sp0 == sp2) << '
';
cout << "sp0 != sp1:" << (sp0 != sp1) << '
';
cout << "sp0 != sp2:" << (sp0 != sp2) << '
';
return 0;
}
template<class Ty1, class Ty2>
bool operator<(
const shared_ptr<Ty1>& left,
const shared_ptr<Ty2>& right);
The function defines a strict weak ordering—as defined in [lib.alg. sorting]
in the C++ standard—on shared_ptr
objects, with the additional constraint that !(left < right) && !(right < left)
is true only if left
and right
are both empty or both control the same resource.
Because operator<
defines a strict weak ordering, you can use shared_ptr
objects as keys in associative containers.
Example 2.18. Less-Than Comparison (smartptr/splt.cpp
)
#include <algorithm>
#include <memory>
#include <iostream>
#include <set>
using std::tr1::shared_ptr;
using std::lower_bound; using std::set;
using std::cout;
typedef shared_ptr<int> spi;
typedef set<spi> iset;
typedef iset::const_iterator citer;
static void lookup (const iset& data, spi sp)
{ // look for stored object that matches sp
citer res = lower_bound(data.begin(), data.end(), sp);
cout << *sp;
if (res == data.end () || * res != sp)
cout << "not found
";
else
cout << "found
";
}
int main()
{ // demonstrate less-than comparison
iset data;
spi sp0(new int(0));
spi sp1(new int(1));
spi sp2(new int(2));
spi sp3(sp1); // shares ownership with sp1
spi sp4(new int(1)); // same value as sp1, but different resource
data.insert (sp0);
data.insert (sp1);
data.insert (sp2);
lookup (data, sp1); // search for sp1
lookup (data, sp3); // search for sp3
lookup (data, sp4); // search for sp4
return 0;
}
In this example, the call to lookup(data, sp1)
succeeds because sp1
is in the container.[4] Similarly, the call to lookup(data, sp3)
succeeds because sp3
is a copy of sp1
, so it owns the same resource as sp1
. However, the call to lookup(data, sp4)
fails;sp4
owns a different resource, even though the two resources hold the same value.
As we’ve just seen, !(left < right) && !(right < left)
is true only if left
and right
are both empty or both control the same resource. Let’s use that relation to define a new function template equiv
, as follows:
template <class Ty1, class Ty2>
bool equiv(shared_ptr<Ty1> left, shared_ptr<Ty2> right)
{
return !(left < right) && !(right < left);
}
That is, equiv
returns true
only if left
and right
are both empty or both control the same resource. As the name suggests, it is an equivalence relation; however, it’s not the same equivalence relation as the one given by operator==
. The member function shared_ptr::get()
returns a null pointer when called on a shared_ptr<Ty>
object that was constructed with the default constructor. It also returns a null pointer when called on a shared_ptr<Ty>
object that was constructed with a null pointer, so an object constructed with the default constructor will compare equal to an object constructed with a null pointer. These two objects do not control the same resource, however, so a call to equiv
with these two objects will return false
. We’ll look at this in more detail in one of the exercises.
shared_ptr
Objects into Streamstemplate<class Elem, class Tr, class Ty>
std::basic_ostream<Elem, Tr>& operator<<(
std::basic_ostream<Elem, Tr>& str,
const shared_ptr<Ty>& sp);
The function inserts sp.get()
into the stream str
.
Example 2.19. Stream Inserter (smartptr/inserter.cpp
)
#include <memory>
#include <iostream>
using std::tr1::shared_ptr;
using std::cout;
int main()
{ // demonstrate stream inserter
shared_ptr<int> sp(new int);
cout << " get: " << sp.get() < < '
';
cout << "object: " << sp << '
';
return 0;
}
If you have an object of type shared_ptr<char>
, the stream inserter will insert the resulting pointer to char
. Just aswitha char*
, if the pointer doesn’t point to a null-terminated byte string, the result is unpredictable.
get_deleter
template<class D, class Ty>
D *get_deleter(const shared_ptr<Ty>& sp);
If the argument sp
has a deleter of type D
, the function template returns a pointer to that deleter object; otherwise, it returns a null pointer.
Example 2.20. get_deleter
(smartptr/getdeleter.cpp
)
#include <memory>
#include <iostream>
using std::tr1::shared_ptr; using std::tr1::get_deleter;
using std::cout;
struct del
{ // trivial deleter object
void operator ()(void *ptr)
{ // simply delete
delete ptr;
}
};
int main()
{ // demonstrate function function get_deleter
shared_ptr<int> sp0(new int); // no deleter
shared_ptr<int> sp1(new int, del()); // has deleter
cout << get_deleter <del>(sp0) << '
';
cout << get_deleter <del>(sp1) << '
';
return 0;
}
weak_ptr
Class Templatetemplate<class Ty> class weak_ptr {
public:
typedef Ty element_type;
weak_ptr();
weak_ptr(const weak_ptr & wp);
template<class Other>
weak_ptr(const weak_ptr <Other>& wp);
template<class Other>
weak_ptr(const shared_ptr <Other>& sp);
~weak_ptr();
weak_ptr& operator=(const weak_ptr & wp);
template<class Other>
weak_ptr& operator=(const weak_ptr<Other>& wp);
template<class Other>
weak_ptr& operator=(shared_ptr <Other>& sp);
void swap(weak_ptr & wp);
void reset();
long use_count() const;
bool expired() const;
shared_ptr<Ty> lock() const;
};
Objects of type weak_ptr<Ty>
are used to break cycles in data structures.
A cycle occurs when two or more controlled resources hold pointers to one another so that the pointers form a loop. For example, if a node head
contains a shared_ptr
object that points to another node, N1
, and the node N1
contains a shared_ptr
object that points to head
, the two nodes form a cycle. Because each node holds a pointer to the other, neither of the two reference counts can ever reach zero. The two nodes will not be deleted, even if no other shared_ptr
objects are pointing to either of them. To break this cycle, N1
should hold a weak_ptr
object that points to head
, rather than a shared_ptr
object. The code can still get from N1
back to head
through the weak_ptr
object, but when the last shared_ptr
object that points to head
is destroyed, the reference count for head
will become zero, and head
will be deleted; its destructor also destroys its shared_ptr
object that points to N1
, so the reference count for N1
will also become zero, and N1
will be destroyed.
Example 2.21. Weak Pointers (smartptr/weakptr.cpp
)
#include <iostream>
#include <memory>
#include "sputil.h"
using std::tr1::shared_ptr; using std::tr1::weak_ptr;
using std::cout;
struct node : instrumented
{ // struct to demonstrate cycles
shared_ptr<node> next;
weak_ptr<node> weak_next;
};
static void cycle ()
{ // constructs two nodes, destroys none
cout << "Cycle:
";
node *head = new node;
node *N1 = new node;
shared_ptr<node> root(head);
head->next = shared_ptr<node>(N1);
N1->next = root; // cycle
}
static void no_cycle ()
{ // constructs two nodes, destroys both
cout << "Break cycle:
";
node *head = new node;
node *N1 = new node;
shared_ptr<node> root(head);
head->next = shared_ptr<node>(N1);
N1-> weak_next = root; // no cycle
}
int main()
{ // demonstrate creating cycle and breaking cycle
cycle ();
no_cycle ();
return 0;
}
weak_ptr
SummaryAn object of type weak_ptr<Ty>
can be created by the template’s default constructor, by the constructor that takes a shared_ptr
object, and by the constructor that takes another weak_ptr
object. These constructors are discussed in Section 2.5.2.
The destructor for weak_ptr
is discussed in Section 2.5.3.
You can’t get to the controlled resource directly from a weak_ptr
object. You have to create a shared_ptr
object that owns the resource that the weak_ptr
object points to and get to the controlled resource from there. You can create a shared_ptr
object with the shared_ptr
constructor that takes a reference to an object whose type is an instance of weak_ptr
or with the member function weak_ptr::lock
. These are discussed in Section 2.5.4.
To check whether a weak_ptr
object has expired, use the member function expired
. To get the value of the reference count for the controlled resource, use the member function use_count
. These are discussed in Section 2.5.5.
You can change a weak_ptr
object so that it points to a different controlled resource or to nothing. This is done with operator=
, discussed in Section 2.5.6, or with the reset
and swap
member functions or the swap
function template, discussed in Section 2.5.7.
The test for relative order of two weak_ptr
objects is discussed in Section 2.5.8.
weak_ptr
Objectweak_ptr::weak_ptr();
The default constructor constructs an empty weak_ptr
object.
Example 2.22. Default Constructor (smartptr/wpdefcon.cpp
)
#include <memory>
#include "sputil.h"
using std::tr1::weak_ptr;
int main()
{
weak_ptr<int> sp; // default constructor
show("default constructor", sp);
return 0;
}
template<class Other>
weak_ptr::weak_ptr(const shared_ptr <Other>& sp);
The constructor assigns control (Section 2.1) of its argument to the newly constructed shared_ptr
object.
Example 2.23. Construct from shared_ptr
(smartptr/wpspcon.cpp
)
#include <memory>
#include "sputil.h"
using std::tr1::shared_ptr; using std::tr1::weak_ptr;
int main()
{ // demonstrate construction from shared_ptr
shared_ptr <resource> sp(new resource(4));
// sp owns resource
show("shared_ptr", sp);
weak_ptr<resource> wp(sp); // wp points to resource
show("weak_ptr", wp);
return 0;
}
weak_ptr::weak_ptr(const weak_ptr& wp);
template<class Other>
weak_ptr::weak_ptr(const weak_ptr<Other>& wp);
The constructors assign control (Section 2.1) of their arguments to the newly constructed shared_ptr
object.
Example 2.24. Construct from weak_ptr
(smartptr/wpwpcon.cpp
)
#include <memory>
#include "sputil.h"
using std::tr1::shared_ptr; using std::tr1::weak_ptr;
int main()
{ // demonstrate construction from shared_ptr
shared_ptr<resource> sp(new resource(4));
// sp owns resource
weak_ptr<resource> wp0 (sp); // wp0 points to resource
weak_ptr<resource> wp1 (wp0); // wp1 points to resource
show("first weak_ptr", wp0);
show("second weak_ptr", wp1);
return 0;
}
weak_ptr
Objectweak_ptr::~ weak_ptr();
The destructor releases the controlled resource.
See Section 2.9 for details.
shared_ptr
ObjectAs mentioned earlier, you can’t get from a weak_ptr
object to its controlled resource directly; you have to create a shared_ptr
object from that weak_ptr
object. You can do that in two ways. You can construct a shared_ptr
object, and you can call the member function lock
. The key difference between the two is that if the weak_ptr
object has expired, the shared_ptr
constructor will throw an exception, but the lock
member function will return an empty shared_ptr
object.
template<class Other>
shared_ptr::shared_ptr(const weak_ptr<Other>& wp);
If wp
has expired, the constructor throws an object of type bad_weak_ptr
. See Section 2.7 for the definition of this type. If wp
is empty, the resulting shared_ptr
object is also empty. Otherwise, the resulting shared_ptr
object owns the resource that wp
points to.
Example 2.25. Construct a shared_ptr
Object (smartptr/spwpcon.cpp
)
#include <iostream>
#include <memory>
#include "sputil.h"
using std::tr1::shared_ptr; using std::tr1::weak_ptr;
using std::tr1::bad_weak_ptr;
using std::cout;
int main()
{ // demonstrate constructing shared_ptr object from weak_ptr object
weak_ptr<resource> empty_wp;
show("empty weak_ptr", empty_wp);
try
{ // try to construct from empty weak_ptr object
shared_ptr<resource> sp0(empty_wp);
}
catch(const bad_weak_ptr&)
{ // catch resulting exception
cout << "caught bad_weak_ptr object
";
}
shared_ptr<resource> sp((resource *)0);
weak_ptr<resource> wp1(sp);
shared_ptr<resource> sp1(wp1);
show("weak_ptr holding null pointer", wp1);
show("copy of weak_ptr holding null pointer", sp1);
sp.reset(new resource);
weak_ptr<resource> wp2(sp);
shared_ptr<resource> sp2(wp2);
show("weak_ptr holding pointer to resource", wp2);
show("shared_ptr holding pointer to resource", sp2);
shared_ptr<resource> sp3(new resource);
weak_ptr<resource> wp3(sp3);
sp3.reset ();
try
{ // try to construct from expired weak_ptr object
shared_ptr <resource> sp4(wp3);
}
catch(const bad_weak_ptr&)
{ // catch resulting exception
cout << "caught bad_weak_ptr object
";
}
return 0;
}
shared_ptr<Ty> weak_ptr::lock() const;
The member function returns an empty shared_ptr
object if *this
has expired; otherwise, shared_ptr(*this)
.
Example 2.26. weak_ptr::lock
(smartptr/lock.cpp
)
#include <memory>
#include "sputil.h"
using std::tr1::shared_ptr; using std::tr1::weak_ptr;
static void do_lock (const char *title,
weak_ptr<resource> wp)
{
shared_ptr<resource> sp = wp.lock();
show(title, sp);
}
int main()
{ // demonstrate member function lock
shared_ptr<resource> sp0(new resource);
weak_ptr<resource> wp0(sp0);
do_lock("weak_ptr with resource", wp0);
sp0.reset();
do_lock("expired weak_ptr", wp0);
return 0;
}
bool weak_ptr::expired() const;
The member function returns true
only if the weak_ptr
object has expired.
Example 2.27. weak_ptr::expired
(smartptr/expired.cpp
)
#include <iomanip>
#include <iostream>
#include <memory>
#include "sputil.h"
using std::tr1::shared_ptr; using std::tr1::weak_ptr;
using std::cout; using std::boolalpha;
int main()
{ // demonstrate member function expired
cout << boolalpha;
shared_ptr<resource> sp(new resource);
weak_ptr<resource> wp(sp);
cout << "points to resource: " << wp.expired () << '
';
sp.reset ();
cout << "expired: " << wp.expired() << '
';
return 0;
}
long weak_ptr::use_count ()const;
The member function returns the number of shared_ptr
objects that own the resource controlled by *this
. If the weak_ptr
object is empty or expired, returns 0.
Example 2.28. weak_ptr::use_count
(smartptr/wpcount.cpp
)
#include <memory>
#include <iostream>
using std::tr1::shared_ptr; using std::tr1::weak_ptr;
using std::cout;
typedef shared_ptr<int> spi;
typedef weak_ptr<int> wpi;
int main()
{ // demonstrate member function use_count
wpi wp0; // empty object
cout << "empty object: " << wp0.use_count() << '
';
spi sp1((int *)0); // no resource
wpi wp1(sp1);
cout << "null pointer: " << wp1.use_count() << '
';
spi sp2(new int); // controls resource
wpi wp2(sp2);
cout << "one object: " << wp2.use_count() << '
';
{ // create short-lived object
spi sp3(sp2); // copy
cout << "two objects: " << wp2.use_count() << '
';
} // sp3 destroyed
cout << "one object: " << wp2.use_count() << '
';
sp2.reset ();
cout << "expired: " << wp2.use_count() << '
';
return 0;
}
weak_ptr
Objectweak_ptr& weak_ptr::operator= (const weak_ptr& wp);
template<class Other>
weak_ptr& weak_ptr::operator=(const weak_ptr <Other>& wp);
template<class Other>
weak_ptr & weak_ptr::operator=(shared_ptr<Other>& sp);
Each of these operators releases the resource controlled by *this
, if any, and assigns control (Section 2.1) of the resource on the right-hand side of the assignment to *this
.
Example 2.29. Assign to a weak_ptr
Object (smartptr/wpasgn.cpp
)
#include <memory>
#include <iostream>
#include "sputil.h"
using std::tr1::shared_ptr; using std::tr1::weak_ptr;
typedef shared_ptr<resource> sps;
typedef weak_ptr<resource> wps;
int main()
{ // demonstrate effects of assignment
sps sp0(new resource(1)); // allocate resource
wps wp0;
wp0 = sp0;
show("assign to wp0", wp0);
sps sp1(new resource(2)); // allocate resource
wps wp1;
wp1 = sp1;
show("assign to wp1", wp1);
wp1 = wp0; // assign
show("assign, wp0", wp0);
show("assign, wp1", wp1);
show("assign, sp0", sp0);
show("assign, sp1", sp1);
return 0;
}
weak_ptr
Objectvoid weak_ptr::reset();
The member function releases control of the object’s controlled resource. After the function returns, *this
is empty.
Example 2.30. reset (smartptr/wpreset.cpp
)
#include <memory>
#include "sputil.h"
using std::tr1::shared_ptr; using std::tr1::weak_ptr;
int main()
{ // demonstrate member function reset()
weak_ptr<resource> wp0;
show("empty object before reset", wp0);
wp0.reset();
show("empty object after reset", wp0);
shared_ptr<resource> sp1(new resource(1));
weak_ptr<resource> wp1(sp1);
show("non-empty object before reset", wp1);
wp1.reset();
show("non-empty object after reset", wp1);
return 0;
}
void weak_ptr::swap (weak_ptr& wp);
template<class Ty>
void swap(shared_ptr<Ty>& left, shared_ptr<Ty>& right);
The member function swaps the controlled resources between *this
and wp
. The function template executes left.swap(right)
.
Example 2.31. swap
Functions (smartptr/wpswap.cpp
)
#include <memory>
#include "sputil.h"
using std::tr1::shared_ptr; using std::tr1::weak_ptr;
int main()
{ // demonstrate member function swap
shared_ptr<resource> sp0(new resource(0));
weak_ptr<resource> wp0(sp0);
shared_ptr<resource> sp1(new resource(1));
weak_ptr<resource> wp1(sp1);
show("wp0 before swap", wp0);
show("wp1 before swap", wp1);
wp0.swap(wp1);
show("wp0 after swap", wp0);
show("wp1 after swap", wp1);
swap(wp0, wp1);
show("wp0 after second swap", wp0);
show("wp1 after second swap", wp1);
return 0;
}
weak_ptr
Objectstemplate<class Ty1, class Ty2>
bool operator<(
const weak_ptr<Ty1>& left,
const weak_ptr<Ty2>& right);
The function defines a strict weak ordering—as defined in [lib.alg. sorting]
in the C++ standard—on shared_ptr
objects, with the additional constraint that !(left < right) && !(right < left)
is true only if left
and right
control the same resource.
Because operator<
defines a strict weak ordering, you can use shared_ptr
objects as keys in associative containers. See also Section 2.4.8.
Example 2.32. Less-Than Comparison (smartptr/wplt.cpp
)
#include <algorithm>
#include <memory>
#include <iostream>
#include <set>
using std::tr1::shared_ptr; using std::tr1::weak_ptr;
using std::lower_bound; using std::set;
using std::cout;
typedef shared_ptr<int> spi;
typedef weak_ptr<int> wpi;
typedef set<wpi> iset;
typedef iset::const_iterator citer;
static void lookup(const iset& data, wpi wp)
{ // look for stored object that matches wp
citer res = lower_bound(data.begin(), data.end(), wp);
spi sp0(wp);
cout << *sp0;
if (res == data.end() || spi (*res) != sp0)
cout << " not found
";
else
cout << " found
";
}
int main()
{ // demonstrate less-than comparison
iset data;
spi sp0(new int(0));
spi sp1(new int(1));
spi sp2(new int(2));
spi sp3(sp1); // shares ownership with sp1
spi sp4(new int(1)); // same value as sp1,
// but different resource
data.insert(wpi(sp0));
data.insert(wpi(sp1));
data.insert(wpi(sp2));
lookup(data, wpi(sp1)); // search for sp1
lookup(data, wpi(sp3)); // search for sp3
lookup(data, wpi(sp4)); // search for sp4
return 0;
}
enable_shared_from_this
Class Templatetemplate<class Ty>
class enable_shared_from_this {
public:
shared_ptr<Ty> shared_from_this();
shared_ptr<const Ty> shared_from_this() const;
protected :
enable_shared_from_this();
enable_shared_from_this(const enable_shared_from_this&);
enable_shared_from_this& operator=(
const enable_shared_from_this&);
~ enable_shared_from_this();
};
The member function shared_ptr::get
gives you a pointer to the resource that a shared_ptr
object owns. To go the other way, getting a shared_ptr
object that owns the resource pointed to by a raw pointer, the obvious approach is to construct a shared_ptr
object. However, if the resource is already owned by another shared_ptr
object, you’ll now have two shared_ptr
objects that know nothing about each other and own the same resource. This will lead to problems, as both of the shared_ptr
objects or their progeny will eventually try to delete the same object. To avoid this problem, types that will be managed through shared_ptr
objects can provide a member function that returns a shared_ptr
object that shares ownership with the original manager object. This is tricky to implement without tampering with the implementation of shared_ptr
, so the class template enable_shared_from_this
provides the member function shared_from_this
.
To use the template enable_shared_from_this
with a class Ty
, make enable_shared_from_this<Ty>
a public base of Ty
:
class C : public enable_shared_from_this <C>
{
// …
};
Now if you create an object of type shared_ptr<C>
from a pointer to a fully constructed object of type C
[5] the shared_ptr
code will make a note in the enable_shared_from_this
base subobject, and its member function shared_from_this
will return a shared_ptr<C>
object that shares ownership with the shared_ptr<C>
object that you created.
shared_ptr<Ty> enable_shared_from_this::shared_from_this();
shared_ptr<const Ty>
enable_shared_from_this::shared_from_this() const;
The member functions return a shared_ptr
object that shares ownership with the original owner of *this
.
Example 2.33. shared_from_this
(smartptr/enshared.cpp
)
#include <memory>
#include <ostream>
#include "sputil.h"
using std::tr1::shared_ptr;
using std::tr1::enable_shared_from_this;
using std::basic_ostream;
struct C : enable_shared_from_this <C>, resource
{
C(int i0 = 0) : resource(i0) {}
};
static void show_sp(C * cp)
{
shared_ptr<C>spc(cp->shared_from_this());
show ("from this", spc);
}
int main()
{
shared_ptr<C> sp(new C(1));
show("original object", sp);
C *cp = sp.get();
show_sp(cp);
show("after return", sp);
return 0;
}
enable_shared_from_this::enable_shared_from_this();
enable_shared_from_this::enable_shared_from_this(
const enable_shared_from_this&);
enable_shared_from_this& enable_shared_from_this::operator=(
const enable_shared_from_this&);
enable_shared_from_this::~ enable_shared_from_this();
The constructors, destructor, and assignment operator do not throw exceptions.
These members are protected to help prevent accidental misuse. Make sure that your derived class calls them as appropriate.
Example 2.34. enable_shared_from_this
(smartptr/enderive.cpp
)
#include <memory>
#include "sputil.h"
using std::tr1::enable_shared_from_this;
class derived : public enable_shared_from_this<derived>
{
typedef enable_shared_from_this<derived> base;
public:
derived() {} // calls default constructor for base
derived(const derived& der)
: base(der) {} // explicit call to copy constructor for base
derived& operator =(const derived& der)
{
base::operator =(der); // explicit call to assignment operator
}
~derived () {} // calls destructor for base
};
bad_weak_ptr
Classclass bad_weak_ptr: public std::exception {
public:
bad_weak_ptr();
};
The class describes an exception that can be thrown when constructing a shared_ptr
object that is a copy of a weak_ptr
object and when assigning a weak_ptr
object to a shared_ptr
object.
See Section 2.5.2.
In the examples we’ve looked at so far, the type Ty
of the pointer held by a shared_ptr<Ty>
or a weak_ptr<Ty>
has been the same as the type of the resource pointer passed to the original owner of the resource. That’s not required. You can construct a shared_ptr<Ty>
object from a resource pointer Other*
, and you can call shared_ptr::reset
with a resource pointer Other*
, provided that Other*
is convertible to Ty*
. See Section 2.8.1. You can also use copy constructors and assignments to convert shared_ptr<Other>
and weak_-ptr<Other>
objects to shared_ptr<Ty>
and weak_ptr<Ty>
objects, again provided that Other*
is convertible to Ty*
. See Section 2.8.2. Finally, you can call function templates that convert objects of type shared_ptr<Other>
to objects of type shared_ptr<Ty>
in much the same way that static_cast<Ty>
, dynamic_cast<Ty>
, and const_cast<Ty>
do for ordinary pointers. See Section 2.8.3.
template <class Other>
explicit shared_ptr<Ty>::shared_ptr(Other *ptr);
template <class Other, class D>
shared_ptr<Ty>::shared_ptr(Other *ptr, D dtor);
template <class Other>
void shared_ptr<Ty>::reset(Other *ptr);
template <class Other, class D>
void shared_ptr<Ty>::reset(Other *ptr, D dtor);
Each constructor constructs a shared_ptr<Ty>
object that controls the resource pointed to by ptr
.
The argument ptr
must be convertible to a pointer of type Ty*
; if it isn’t, the code won’t compile. The resulting object stores the original value of ptr
; when the last owner of the resource is destroyed, the resource will be destroyed through that pointer. See Section 2.9.
Example 2.35. Pointer Conversions (smartptr/cnvptr.cpp
)
#include <memory>
#include "sputil.h"
using std::tr1::shared_ptr;
int main()
{ // demonstrate pointer conversions
shared_ptr<resource> sp(new d_res(1));
show("constructed from pointer to derived", sp);
sp.reset(new d_res(2));
show("reset with pointer to derived", sp);
return 0;
}
template <class Other>
shared_ptr<Ty>::shared_ptr(const shared_ptr<Other>& wp);
template<class Other>
weak_ptr<Ty>::weak_ptr(const shared_ptr <Other>& sp);
template<class Other>
explicit shared_ptr<Ty>::shared_ptr(
const weak_ptr<Other>& wp);
template <class Other>
weak_ptr<Ty>::weak_ptr(const weak_ptr <Other>& wp);
template <class Other>
shared_ptr<Ty>& shared_ptr<Ty>::operator=(
const shared_ptr<Other>& sp);
template <class Other>
weak_ptr<Ty>& weak_ptr<Ty>::operator=(
const shared_ptr<Other>& sp);
template <class Other>
weak_ptr<Ty>& weak_ptr<Ty>::operator=(
const weak_ptr <Other>& wp);
Each function returns a shared_ptr<Ty>
object or a weak_ptr<Ty>
object that controls the resource that is controlled by its argument.
A pointer of type Other*
must be convertible to a pointer of type Ty*
; if it isn’t, the code won’t compile. The resulting object controls the same resource as the argument.
Example 2.36. Object Conversions (smartptr/cnvobj.cpp
)
#include <memory>
#include "sputil.h"
using std::tr1::shared_ptr; using std::tr1::weak_ptr;
int main()
{ // demonstrate object conversions
shared_ptr<d_res> spa(new d_res(1));
weak_ptr<d_res> wpa(spa);
shared_ptr<resource> sp0(spa);
show("shared_ptr constructed from shared_ptr<d_res>",
sp0);
weak_ptr<resource> wp0(spa);
show("weak_ptr constructed from shared_ptr<d_res>",
wp0);
shared_ptr<resource> sp1(wpa);
show("shared_ptr constructed from weak_ptr<d_res>",
sp1);
weak_ptr<resource> wp1(wpa);
show("weak_ptr constructed from weak_ptr<d_res>",
wp1);
shared_ptr<d_res> spb(new d_res(2));
weak_ptr<d_res> wpb(spb);
sp0 = spb;
show("shared_ptr assigned from shared_ptr<d_res>",
sp0);
wp0 = spb;
show("weak_ptr assigned from shared_ptr<d_res>",
wp0);
wp1 = wpb;
show("weak_ptr assigned from weak_ptr<d_res>",
wp1);
return 0;
}
The conversions in the previous two sections are implicit conversions. They require the pointer type Other*
to be implicitly convertible to the pointer type Ty*
, so they act very much like that implicit pointer conversion. Just as with raw pointers, you’ll sometimes need to convert shared_ptr
objects to types that go the other way, that is, conversions that aren’t inherently typesafe. With raw pointers, you make these conversions with various forms of casts; with shared_ptr
objects, you use the function templates static_pointer_-cast
, dynamic_pointer_cast
, and const_pointer_cast
.
template <class Ty, class Other>
shared_ptr<Ty> static_pointer_cast(
const shared_ptr<Other>& sp);
The function template returns shared_ptr<Ty>()
if sp
is empty; otherwise, it returns an object that acts like shared_ptr<Ty>(static_cast <Ty*>(sp.get())
but shares ownership of the resource that sp
points to. If the expression static_cast<Ty*>(sp.get())
is not valid, the function is not valid.
Example 2.37. static_pointer_cast
(smartptr/cnvstatic.cpp
)
#include <memory>
#include "sputil.h"
using std::tr1::shared_ptr;
using std::tr1::static_pointer_cast;
int main()
{ // demonstrate static_pointer_cast
shared_ptr<resource> sp(new d_res(1));
shared_ptr<d_res> sp0 = static_pointer_cast<d_res>(sp
show ("base resource", sp);
show ("derived resource", sp0);
sp. reset();
sp0 = static_pointer_cast <d_res>(sp);
show ("null pointer to base resource", sp);
show ("null pointer to derived resource", sp0);
return 0;
}
template <class Ty, class Other>
shared_ptr<Ty> dynamic_pointer_cast (
const shared_ptr<Other>& sp);
The function template returns shared_ptr<Ty>()
if sp
is empty or if dynamic_cast<Ty*>(sp.get())
returns 0; otherwise, it returns an object that acts like shared_ptr<Ty>(dynamic_cast<Ty*>(sp.get())
, but shares ownership of the resource that sp
points to. If the expression dynamic_cast<Ty*>(sp.get())
is not valid, the function is not valid.
Example 2.38. dynamic_pointer_cast
(smartptr/cnvdyn.cpp
)
#include <memory>
#include "sputil.h"
using std::tr1::shared_ptr;
using std::tr1::dynamic_pointer_cast;
struct base0
{ // simple base class
base0(int i0) : i(i0) {}
virtual ~base0() {}
int i;
};
template <class Elem, class Tr>
std::basic_ostream<Elem, Tr>& operator <<(
std::basic_ostream<Elem, Tr>& str,
const base0& b0)
{ // insert base0 contents into stream
return str << b0.i;
}
struct base1
{ // simple base class
base1(int j0) : j(j0) {}
virtual ~base1 () {}
int j;
};
template <class Elem, class Tr>
std::basic_ostream<Elem, Tr>& operator<<(
std::basic_ostream<Elem, Tr>& str,
const base1& b1)
{ // insert base1 contents into stream
return str << b1.j;
}
struct derived : virtual base0, base1
{ // derived class
derived (int i0, int j0, int k0)
: base0 (i0), base1 (j0), k(k0) {}
int k;
};
template <class Elem, class Tr>
std::basic_ostream <Elem, Tr>& operator <<(
std::basic_ostream<Elem, Tr>& str,
const derived& d)
{ // insert derived contents into stream
return str << d.k;
}
int main()
{
shared_ptr<base0> sp(new derived(1, 2, 3));
show("base0 shared_ptr", sp);
shared_ptr<derived> sp0 =
dynamic_pointer_cast<derived>(sp);
show("upcast from virtual base", sp0);
shared_ptr<base1> sp1 =
dynamic_pointer_cast<base1>(sp);
show("cross -cast", sp1);
shared_ptr<resource> sp2 =
dynamic_pointer_cast<resource>(sp);
show("failed cast", sp2);
return 0;
}
template <class Ty, class Other>
shared_ptr<Ty> const_pointer_cast(
const shared_ptr<Other>& sp);
The function template returns shared_ptr<Ty>()
if sp
is empty; otherwise, it returns an object that acts like shared_ptr<Ty>(const_cast <Ty*>(sp.get())
but shares ownership of the resource that sp
points to. If the expression const_cast<Ty*>(sp.get())
is not valid, the function is not valid.
Example 2.39. const_pointer_cast
(smartptr/cnvconst.cpp
)
#include <memory>
#include "sputil.h"
using std::tr1::shared_ptr;
using std::tr1::const_pointer_cast;
int main()
{ // demonstrate pointer conversions
shared_ptr<const resource> sp(new resource (1));
show("shared_ptr to const object", sp);
shared_ptr<resource> sp0 =
const_pointer_cast<resource>(sp);
show ("shared_ptr to non-const object", sp0);
return 0;
}
When the last shared_ptr
object that owns a resource releases control of that resource, the resource will be destroyed. This is done with delete
if the resource does not have a deleter; otherwise, with the deleter object. In both cases, the resource is destroyed through the original pointer to that resource, even if the type of that pointer is different from the template’s type argument Ty
.[6]
A resource that does not have a deleter object is destroyed by deleting the original pointer to that resource.
Example 2.40. Destroy a Resource, no Deleter (smartptr/destdtor.cpp
)
#include <memory>
#include <iostream>
#include "sputil.h"
using std::tr1::shared_ptr;
using std::cout;
struct base0
{
base0 () : i(0) {}
~base0 ()
{
cout << "destroying base0 at"
<< (void*) this << '
';
}
int i;
};
struct base1
{
base1 () : j(1) {}
~base1 ()
{
cout << "destroying base1 at"
<< (void*) this << '
';
}
int j;
};
template <class Elem, class Tr>
std::basic_ostream<Elem, Tr>& operator <<(
std::basic_ostream<Elem, Tr>& str,
const base1& b1)
{ // insert base1 contents into stream
return str << b1.j;
}
struct derived : base0, base1
{
~derived ()
{
cout << "destroying derived at"
<< (void*) this << '
';
}
};
int main()
{
shared_ptr<base1> sp(new derived);
show("shared_ptr object", sp);
return 0;
}
If you compile and run this example, you’ll see that the shared_ptr<base1>
object holds the address of the base1
subobject of the derived
object. Nevertheless, when it goes out of scope at the end of main
, its destructor destroys the resource through the original pointer to derived
; the destructor for derived
runs first, followed by the destructors for base0
and base1
.[7]
A resource that has a deleter object is destroyed by calling the deleter object’s function call operator with the original pointer to that resource as argument. The deleter object is responsible for cleaning up the resource.
Example 2.41. Destroy a Resource with Deleter (smartptr/deleter.cpp
)
#include <memory>
#include <iostream>
#include <ostream>
#include "sputil.h"
using std::tr1::shared_ptr;
using std::cout;
resource *get_resource(int i)
{ // allocate resource object
resource *res = new resource(i);
cout << "created resource with value"
<< *res << '
';
return res;
}
void destroy_resource (resource *res)
{ // destroy resource object
cout << "destroying resource with value"
<< *res << '
';
delete res;
}
int main()
{ // demonstrate function pointer as deleter object
shared_ptr<resource> sp(get_resource(3),
destroy_resource);
cout << "In main, resource has value "
<< *sp << '
';
return 0;
}
In the main
function, the constructor for sp
is called with the pointer returned by get_resource
and a pointer to the function destroy_resource
. As a result, when sp
goes out of scope, its destructor calls destroy_resource
, passing the original pointer as its argument.
In this example, the function takes an argument whose type is the same as the type of the original pointer. In general, this need not be the case; all that’s needed is that the deleter object can be called with a pointer of that type. For example, we could have used a type with a function call operator that takes a pointer to void
:
struct resource_deleter
{
void operator()(void *ptr) { delete (resource *) ptr ; }
};
The deleter object is passed by value, so all we need to do to use this type is to create a temporary object:
shared_ptr<resource> sp (get_resource(3),
resource_deleter());
When created, the first shared_ptr
object that controls a particular resource allocates a control block from the heap. This allocation might fail, and the allocation code would throw an exception object of type bad_alloc
. When that happens, the shared_ptr
code that is creating the block will destroy the resource and rethrow the exception. This prevents memory leaks in typical code that looks like this:
shared_ptr<resource> sp(new resource(3));
If shared_ptr
didn’t do this, you’d have to store the pointer returned by new
, enter a try
block, initialize the shared_ptr
object, and handle the possible exception by destroying the object. It’s much easier to have that code in one place, inside the implementation of shared_ptr
.
Example 2.42. Exceptions (smartptr/exceptions.cpp
)
#include <memory>
#include <stdlib.h>
#include <iostream>
#include <new>
#include "sputil.h"
using std::tr1::shared_ptr;
using std::bad_alloc ; using std::cout;
static bool no_memory = false;
void *operator new(size_t sz)
{ // allocate memory, with one-shot failure
void *res;
bool no_mem = no_memory;
no_memory = false;
if (no_mem || (res = malloc (sz)) == 0)
throw bad_alloc ();
return res;
}
void operator delete (void *ptr)
{ // free allocated memory
free(ptr);
}
int main()
{ // demonstrate resource destruction on exception
try { // construct with no memory
cout << "construct with no memory :
";
instrumented *ip = new instrumented;
no_memory = true;
shared_ptr<instrumented> sp0(ip);
}
catch(…)
{ // handle the exception
cout << " caught the exception
";
}
try { // reset with no memory
cout << "reset with no memory:
";
shared_ptr<instrumented> sp1;
instrumented *ip = new instrumented;
no_memory = true;
sp1.reset(ip);
}
catch(…)
{ // handle the exception
cout << " caught the exception
";
}
return 0;
}
Neither the C++ standard nor the TR1 library makes any promises about the behavior of C++ code in multithreaded applications (see Appendix C). However, library implementations typically try not to get in the way if you need to write multithreaded applications. This usually means avoiding coding practices that aren’t safe in multithreaded applications. However, making shared_ptr
and weak_ptr
thread-safe requires internal synchronization, to prevent simultaneous conflicting changes to the reference counters. In practice, this means that shared_ptr
and weak_ptr
implementations that are thread-safe could be rather slow.[8]
For each of the following errors, write a simple test case containing the error, and try to compile it. In the error messages, look for the key words that relate to the error in the code.
1. Attempting to construct a shared_ptr
object with a pointer type that is not convertible to the pointer type held by the shared_ptr
2. Attempting to construct a shared_ptr
object with another shared_ptr
object that holds a pointer type that is not convertible to the pointer type held by the object being constructed
3. Attempting to construct a shared_ptr
object with a pointer and a deleter object whose function call operator can’t be called with the original pointer
4. Attempting to convert a shared_ptr<int>
to a shared_ptr<double>
with static_pointer_cast
Write a program that demonstrates the use of shared_ptr
accessors. Begin by defining a simple struct that has one public data member and a default constructor that initializes that data member. Create a shared_ptr
object that points to an object of this type. Display the value of the stored object’s data member three times: once using shared_ptr::get()
, once using shared_ptr::operator*
, and once using shared_ptr::operator->
.
How many different ways can you think of to determine whether a shared_ptr
object holds a non-null pointer? Write a program to verify that they all work correctly.
Write a program consisting of a type definition, a function template, and a main
function. The type definition should be a class whose constructor and destructor write logging messages to cout
. (Use the instrumented
class from sputil.h
if you like). The function template should take an argument of an arbitrary type by value. Its body should simply write a logging message to cout
, showing that it was called. In main
, create an object of your logging type on the heap, and create a shared_ptr
object to manage it. Call the function template with this shared_ptr
object. Add logging messages to main
so that you can see when the heap object is destroyed relative to the call to the function template. Do the same thing using an auto_ptr
object to manage the heap object. Explain the output.
Write a program that allocates an array of objects on the heap and manages that array with a shared_ptr
object. Check that the array is properly deleted when it is no longer in use.
I said in Section 2.4.2 that you shouldn’t construct two shared_ptr
objects from the same pointer. The danger is that both shared_ptr
objects or their progeny will eventually try to delete the resource, and that usually leads to trouble. In fact, you can do this if you’re careful. It’s not particularly useful, but write a program that constructs two shared_ptr<instrumented>
objects from the same pointer and deletes the resource only once.
Write a program consisting of two functions—the main
function and a function named write
. The write
function should take an argument of type shared_ptr<FILE>
and should write some text to the C-style stream identified by the FILE*
held in the shared_ptr
object. (Use the member function get()
to get the pointer.) The main
function should create two shared_ptr<FILE>
objects. One should hold the pointer returned by a call to fopen
and should properly close the stream when the last shared_ptr
object managing that file is destroyed. The other should hold the pointer stdout
and should not close the stream. The main
function should call write
with each of shared_ptr
objects.
Assume that you have the following objects:
// EMPTY shared_ptr OBJECTS
shared_ptr<int> sp0;
shared_ptr<int> sp1 (sp0);
shared_ptr<int> sp2;
// shared_ptr OBJECTS HOLDING NULL POINTERS
shared_ptr<int> sp3 ((int *)0);
shared_ptr<int> sp4 (sp3);
shared_ptr<int> sp5 ((int *)0);
// shared_ptr OBJECTS HOLDING NON-NULL POINTERS
shared_ptr<int> sp6 (new int (3));
shared_ptr<int> sp7 (sp6);
shared_ptr<int> sp8 (new int (3));
1. What should the result of the following comparisons be?
(a) sp0 == sp1
(b) sp0 == sp2
(c) sp0 == sp3
(d) sp3 == sp4
(e) sp3 == sp5
(f) sp0 == sp6
(g) sp6 == sp7
(h) sp6 == sp8
2. Write a program to verify your answers.
3. Consider the following function:
template <class Ty1, class Ty2>
bool equiv(shared_ptr<Ty1> left,
shared_ptr<Ty2> right)
{
return !(left < right) && !(right < left);
}
Logically, it makes sense to say that left
and right
are equivalent if left
is not less than right
and right
is not less than left
. In fact, since operator<
for shared_ptr
types defines a strict weak ordering, it can be shown that equiv
in turn is an equivalence relation, as its name suggests. In Section 2.4.8, we saw that this equivalence relation is required to be true only for objects that are empty or share ownership of the same resource. That’s different from the rule for operator==
. What should the result of the following function calls be?
(a) equiv(sp0, sp1)
(b) equiv(sp0, sp2)
(c) equiv(sp0, sp3)
(e) equiv(sp3, sp5)
(f) equiv(sp0, sp6)
(g) equiv(sp6, sp7)
(h) equiv(sp6, sp8)
1. Write a program to verify your answers.
1. Use the function template equiv
given in the previous exercise and the following data types and data objects:
// TYPES
struct base1 {};
struct base2 {};
struct derived : base1, base2 {};
// OBJECTS
shared_ptr<derived> sp0(new derived);
shared_ptr<base1> sp1(sp0);
shared_ptr<base2> sp2(sp0);
shared_ptr<derived> sp3(new derived);
What should the result of the following function calls be?
(a) equiv(sp0, sp1)
(b) equiv(sp0, sp2)
(c) equiv(sp1, sp2)
(d) equiv(sp3, sp1)
(e) equiv(sp3, sp2)
Write a program to verify your answers.
2. What should happen if you use ==
instead of equiv
for the comparisons? Write a program to verify your answers.
Write a program consisting of a function named do_search
and a main
function. The do_search
function should take one argument of type shared_ptr<int>
and one argument of type const std::vector<shared_ptr<int>>&
. Assuming that the contents of the vector
object are sorted, it should search for the shared_ptr<int>
object in the vector
object twice, first using the std::find
algorithm and then using the std::binary_search
algorithm, and report the results of both searches.
The main
function should create several objects of type shared_ptr<int>
, with each holding a pointer to an int
object allocated on the heap. These int
objects should all have different values. It should also create a shared_ptr<int>
object that holds a null pointer and an empty shared_ptr<int>
object. It should insert each of those shared_ptr<int>
objects into an std:: vector<shared_ptr<int>>
and use std::sort
to sort the contents of the vector. Then it should do the following:
1. Iterate through the container, showing the value, in order, of each of the int
objects that the container elements control. Be careful not to dereference any null pointers.
2. Call do_search
, passing the empty shared_ptr
object and the vector
object.
3. Call do_search
, passing the shared_ptr
object that holds a null pointer and the vector
object.
4. Call do_search
, passing one of the shared_ptr
objects that holds a non-null pointer and the vector
object.
5. Create another empty shared_ptr<int>
object that is not a copy of the original one, and call do_search
with the new object and the vector
object.
6. Create another shared_ptr<int>
object that holds a null pointer and is not a copy of the original one, and call do_search
with the new object and the vector
object.
7. Create another shared_ptr<int>
object that holds a pointer to an int
object allocated on the heap and is not a copy of any of the original ones. The value of the int
object should be the same as the value of one of the original ones. Call do_search
with the new object and the vector
object.
Explain the results.
A double-linked list consists of nodes that hold a data item, a pointer to the next node in the list, and a pointer to the previous node in the list. For purposes of this exercise, the last node in the list stores a null pointer as its pointer to its next node, and the first node in the list stores a null pointer as its pointer to its previous node. You access the contents of the list through a pointer that points to the first node in the list.
1. Write a class template template <class Data> node
that holds an object of type Data
named data
and two pointers named next
and prev
that point to other objects of type node<Data>
. Write another class template, template <class Data> list
, that holds a pointer named head
that points to an object of type node<Data>
. The constructor for this template should initialize head
to 0. Add a member function void list::insert(const Data& val)
that inserts a new node<Data>
object holding the value val
at the head of the linked list. Add a member function bool list::remove(const Data& val)
that removes all nodes whose data
member is equal to val
from the list. Make sure that list
’s destructor deletes any nodes remaining in the list.
2. Rewrite the previous example, using objects of type shared_ptr<node <Data>>
instead of pointers in the template node
and in place of the pointer named head
in the template list
. Remove the destructor from list
; it shouldn’t be needed, because you’re now using shared_ptr
objects instead of pointers. Why doesn’t this version of list
destroy the remaining nodes when the list
object goes out of scope?
3. Fix the memory leak in the previous example.
Write three functions that each take a weak_ptr
object and return a shared_ptr
object. If the weak_ptr
object has expired, the shared_ptr
object returned by each function should be empty; otherwise, it should own the resource that the weak_ptr
argument points to. Each function should, of course, use a different technique to make this determination. Write a program to verify that all three functions work correctly.
In Section 2.8, we talked about implicit conversions between shared_ptr
types and the three function templates for explicit conversions. Consider this class hierarchy:
class A { virtual void f () {} };
class B {};
class G : public A {};
class H : public B {};
class I : public virtual A {};
class J : public virtual B {};
class Z : public G, public H,
public I, public J {};
1. Beginning with an object shared_ptr<Z> spz(new Z)
, which of the following assignments is valid as written, which can be made valid with an explicit conversion, and which ones are simply not allowed?
(a) shared_ptr<G> spg = spz;
(b) shared_ptr<Z> t0 = spg;
(c) shared_ptr<A> spa0 = spg;
(d) shared_ptr<G> t1 = spa0;
(e) shared_ptr<H> sph = spz;
(f) shared_ptr<const B> spb0 = sph;
(g) shared_ptr<H> t2 = spb0;
(h) shared_ptr<I> spi = spz;
(i) shared_ptr<A> spa1 = spi;
(j) shared_ptr<I> t3 = spa1;
(k) shared_ptr<J> spj = spz;
(l) shared_ptr<B> spb1 = spj;
(m) shared_ptr<J> t4 = spb1;
(n) shared_ptr<G> spgx = spb1;
(o) shared_ptr<H> sphx = spa1;
2. Write and compile some code to verify your answers.
3.144.222.185