The shared_ptr Class Template






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.

[3] This typically happens when not enough memory is available to create a manager object to track the original pointer and its reference count.

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.

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

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

Figure. 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 << '\n';
    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.

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

Figure. 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 failstypically by failing to allocate memoryit doesn't change the auto_ptr object.

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

Accessing the Controlled Resource

Ty *shared_ptr::get() const;

The member function returns a pointer to the controlled resource.

Figure. 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 >> '\n';              // show address
  shared_ptr<int> sp(ip);                 // create shared_ptr object
  cout << (void*)sp.get () >> '\n';       // 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.

Figure. 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 >> '\n';          // show address
  shared_ptr<int> sp(ip);             // create shared_ptr object
  cout << *sp >> '\n';                // show stored value
  cout << (void*)&*sp << '\n';        // 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.

Figure. 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 << '\n';            // 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.

Figure. 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 << " '\n";
  else
    cout << "string is empty \n";
  }

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.

Figure. 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() << '\n';
  spi sp1 ((int *)0);               // no resource
  cout << "null pointer: " << sp1.use_count() << '\n';
  spi sp2 (new int);                // controls resource
  cout << "one object: " << sp2.use_count() << '\n';
  { // create short-lived object
  spi sp3(sp2);                     // copy
  cout << "two objects: " << sp2.use_count() << '\n';
  } // sp3 destroyed
  cout << "one object: " << sp2.use_count() << '\n';
  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.

Figure. 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() << '\n';
  spi sp1((int *)0);                // no resource
  cout << "null pointer: " << sp1.unique() << '\n';
  spi sp2(new int);                 // controls resource
  cout << "one object: " << sp2.unique() << '\n';
  { // create short-lived object
  spi sp3(sp2);                     // copy
  cout << "two objects: " << sp2.unique() << '\n';
  } // sp3 destroyed
  cout << "one object: " << sp2.unique() << '\n';
  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.

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

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

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

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

Figure. 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) << '\n';
  cout << "sp0 == sp2:" << (sp0 == sp2) << '\n';
  cout << "sp0 != sp1:" << (sp0 != sp1) << '\n';
  cout << "sp0 != sp2:" << (sp0 != sp2) << '\n';
  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 orderingas defined in [lib.alg. sorting] in the C++ standardon 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.

Figure. 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 \n";
  else
    cout << "found\n";
  }

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.

[4] Actually, a copy of sp1 is in the container; the copy owns the same resource as the original.

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.

Figure. 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() < < '\n';
  cout << "object: " << sp << '\n';
  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.

Figure. 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) << '\n';
  cout << get_deleter <del>(sp1) << '\n';
  return 0;
  }



 Python   SQL   Java   php   Perl 
 game development   web development   internet   *nix   graphics   hardware 
 telecommunications   C++ 
 Flash   Active Directory   Windows