Chapter 2. Smart Pointers

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).

2.1. Definitions

This section defines some terms used to explain the details of how shared_ptrs and weak_ptrs 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.

2.2. About the Examples

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.

2.3. Header <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);

} }

2.4. The shared_ptr Class Template

template<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;
  };

2.4.1. shared_ptr Summary

The 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.

2.4.2. Constructing a shared_ptr Object

shared_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


template<class Other>
  shared_ptr::shared_ptr(const weak_ptr<Other>& wp)

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;
  }


2.4.3. Destroying a shared_ptr Object

shared_ptr::~shared_ptr();

The destructor releases the controlled resource.

See Section 2.9 for details.

2.4.4. Accessing the Controlled Resource

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;
  }


Ty *shared_ptr::operator->() const;

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;
  }


2.4.5. Querying a shared_ptr Object’s State

operator 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;
  }


2.4.6. Assign to a shared_ptr Object

shared_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;
  }


2.4.7. Modify a shared_ptr Object

void 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;
  }


2.4.8. Comparing shared_ptr Objects

Two 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.

Quirks: Empty Objects and Null Pointers

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.

2.4.9. Inserting shared_ptr Objects into Streams

template<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.

2.4.10. Function Template 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;
  }


2.5. The weak_ptr Class Template

template<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;
  }


2.5.1. weak_ptr Summary

An 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.

2.5.2. Constructing a weak_ptr Object

weak_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;
  }


2.5.3. Destroying a weak_ptr Object

weak_ptr::~ weak_ptr();

The destructor releases the controlled resource.

See Section 2.9 for details.

2.5.4. Getting a shared_ptr Object

As 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;
  }


2.5.5. Query an Object’s State

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;
  }


2.5.6. Assigning to a weak_ptr Object

weak_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;
  }


2.5.7. Modifying a weak_ptr Object

void 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;
  }


2.5.8. Comparing weak_ptr Objects

template<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;
  }


2.6. The enable_shared_from_this Class Template

template<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.

2.6.1. Access

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;
  }


2.6.2. Creation, Destruction, and Assignment

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
  };


2.7. The bad_weak_ptr Class

class 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.

2.8. Conversions

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.

2.8.1. Pointer Conversions

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;
  }


2.8.2. Object Conversions

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;
  }


2.8.3. Explicit Conversions

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;
  }


2.9. Destruction of Controlled Resources

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]

2.9.1. Resources without Deleter Objects

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]

2.9.2. Resources with Deleter Objects

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());

2.10. Exceptions

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;
  }


2.11. Multithreading

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]

Exercises

Exercise 1

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

Exercise 2

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->.

Exercise 3

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.

Exercise 4

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.

Exercise 5

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.

Exercise 6

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.

Exercise 7

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.

Exercise 8

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)

(d) equiv(sp3, sp4)

(e) equiv(sp3, sp5)

(f) equiv(sp0, sp6)

(g) equiv(sp6, sp7)

(h) equiv(sp6, sp8)

1. Write a program to verify your answers.

Exercise 9

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.

Exercise 10

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.

Exercise 11

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.

Exercise 12

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.

Exercise 13

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.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.117.70.88