shared ptr

48
Or: How I Learned To Stop Or: How I Learned To Stop Worrying Worrying And Love Resource Management And Love Resource Management Stephan T. Lavavej Stephan T. Lavavej Visual C++ Libraries Developer Visual C++ Libraries Developer 1 Version 1.1 - January 18, 2008

Upload: deepan-dhanakodi

Post on 12-Jan-2016

22 views

Category:

Documents


1 download

DESCRIPTION

boot

TRANSCRIPT

Page 1: Shared Ptr

Or: How I Learned To Stop Or: How I Learned To Stop WorryingWorrying

And Love Resource ManagementAnd Love Resource Management

Stephan T. LavavejStephan T. LavavejVisual C++ Libraries DeveloperVisual C++ Libraries Developer

1Version 1.1 - January 18, 2008

Page 2: Shared Ptr

Boost ComponentsBoost Components shared_ptrshared_ptr (and (and weak_ptrweak_ptr)) mem_fn()mem_fn(), , bind()bind(), and , and functionfunction regexregex <random><random> "Containers": "Containers": tupletuple, , arrayarray, , unordered_setunordered_set (etc.) (etc.) <type_traits><type_traits> reference_wrapperreference_wrapper

C99 Compatibility (C99 Compatibility (<cstdint><cstdint>, etc.), etc.) Special Math Functions (Special Math Functions (riemann_zeta()riemann_zeta(), ,

etc.)etc.)

2Version 1.1 - January 18, 2008

Page 3: Shared Ptr

Effective C++, Third EditionEffective C++, Third Edition (2005) by Scott (2005) by Scott Meyers:Meyers:

""shared_ptrshared_ptr may be the most widely useful may be the most widely useful component in TR1."component in TR1."

C++ Coding StandardsC++ Coding Standards (2005) by Herb (2005) by Herb Sutter and Andrei Alexandrescu:Sutter and Andrei Alexandrescu:

"Store only values and smart pointers in "Store only values and smart pointers in containers. To this we add: If you use containers. To this we add: If you use [Boost] and [C++TR104] for nothing else, [Boost] and [C++TR104] for nothing else, use them for use them for shared_ptrshared_ptr."."

3Version 1.1 - January 18, 2008

Page 4: Shared Ptr

A templated...A templated... non-intrusive...non-intrusive... deterministically reference-counted...deterministically reference-counted... smart pointer...smart pointer... (to a single object)...(to a single object)... that works with polymorphic types...that works with polymorphic types... incomplete types...incomplete types... and STL containers (sequence and and STL containers (sequence and

associative)!associative)!

4Version 1.1 - January 18, 2008

Page 5: Shared Ptr

Generalizing over types without Generalizing over types without forgetting typesforgetting types

shared_ptr<T>shared_ptr<T> "shared pointer to "shared pointer to TT""

shared_ptr<const T>shared_ptr<const T> "shared pointer to "shared pointer to const Tconst T""

const shared_ptr<const T>const shared_ptr<const T> ""constconst shared pointer to shared pointer to const Tconst T""

"Look, Mom! No backwards reading!""Look, Mom! No backwards reading!"

5Version 1.1 - January 18, 2008

Page 6: Shared Ptr

You can instantiate You can instantiate shared_ptr<T>shared_ptr<T> without without modifying the definition of modifying the definition of TT

(That is, the reference count is not embedded)(That is, the reference count is not embedded) Huge usability benefit for minimal perf costHuge usability benefit for minimal perf cost Works with built-in types: Works with built-in types: shared_ptr<int>shared_ptr<int> You can begin using You can begin using shared_ptrshared_ptr in your in your

codebase without having to modify your codebase without having to modify your existing typesexisting types

You can stop using You can stop using shared_ptrshared_ptr for a type for a type without having to rip machinery out of itwithout having to rip machinery out of it

A type can be sometimes held by A type can be sometimes held by shared_ptrshared_ptr and sometimes contained by another typeand sometimes contained by another type

6Version 1.1 - January 18, 2008

Page 7: Shared Ptr

DeterministicDeterministic shared_ptrshared_ptrs collectively share ownership of an s collectively share ownership of an

objectobject When the last When the last shared_ptrshared_ptr dies, the object dies... dies, the object dies... Immediately!Immediately!

Reference-CountedReference-Counted Directed acyclic graphs of Directed acyclic graphs of shared_ptrshared_ptrs to objects s to objects

containing containing shared_ptrshared_ptrs to other objects... are s to other objects... are OKAYOKAY

Cycles of Cycles of shared_ptrshared_ptrs are s are LEAKTROCITYLEAKTROCITY Someone else has to ultimately own youSomeone else has to ultimately own you You can't own yourself!You can't own yourself!

7Version 1.1 - January 18, 2008

Page 8: Shared Ptr

SmartSmart Unlike Unlike auto_ptrauto_ptr, which was a stupid smart pointer, which was a stupid smart pointer Sane copy constructor and copy assignment Sane copy constructor and copy assignment

operatoroperator Behaves like an ordinary value typeBehaves like an ordinary value type Pass and return by value and by reference as usualPass and return by value and by reference as usual Plays nice with Plays nice with constconst

PointerPointer Overloads Overloads operator*()operator*() and and operator->()operator->() Conversion function to Conversion function to unspecified-bool-typeunspecified-bool-type if (sp)if (sp) will compile, will compile, sp * 5sp * 5 will not compile will not compile

No jagged metal edges!No jagged metal edges!

8Version 1.1 - January 18, 2008

Page 9: Shared Ptr

A single object, not an array!A single object, not an array! If you If you newnew up an array and hand it to a up an array and hand it to a shared_ptrshared_ptr:: It will compileIt will compile It will trigger It will trigger UNDEFINED BEHAVIORUNDEFINED BEHAVIOR Which might mean Which might mean LEAKTROCITYLEAKTROCITY or or CRASHTROCITYCRASHTROCITY

If you need...If you need... a container: a container: vectorvector a shared container: a shared container: shared_ptr<vector<T> >shared_ptr<vector<T> > less overhead: less overhead: shared_arrayshared_array (in Boost, but not (in Boost, but not

TR1)TR1) or perhaps: or perhaps: shared_ptr<array<T> >shared_ptr<array<T> > (in TR1) (in TR1)

Custom deleters are insufficient (no Custom deleters are insufficient (no op[]op[]))9Version 1.1 - January 18, 2008

Page 10: Shared Ptr

shared_ptr<Derived>shared_ptr<Derived> is convertible to is convertible to shared_ptr<Base>shared_ptr<Base>

Works fine, doesn't screw up the reference Works fine, doesn't screw up the reference countcount

Need to convert back?Need to convert back? static_pointer_cast<Derived>(spBase)static_pointer_cast<Derived>(spBase) dynamic_pointer_cast<Derived>(spBase)dynamic_pointer_cast<Derived>(spBase)

While we're at it...While we're at it... const_pointer_cast<T>(spConstT)const_pointer_cast<T>(spConstT)

None of these throw exceptions!None of these throw exceptions! There is no There is no reinterpret_pointer_castreinterpret_pointer_cast Note: Note: shared_ptrshared_ptr itself is not polymorphic itself is not polymorphic

10Version 1.1 - January 18, 2008

Page 11: Shared Ptr

struct X;struct X; void fxn(const shared_ptr<X>& p);void fxn(const shared_ptr<X>& p); However, However, XX must be complete by the time must be complete by the time

that you instantiate certain member that you instantiate certain member functions of functions of shared_ptr<X>shared_ptr<X>, such as its , such as its constructor from constructor from X *X *

Reason: If the constructor fails (e.g. to Reason: If the constructor fails (e.g. to allocate memory for a reference count), it allocate memory for a reference count), it must delete the must delete the XX before throwing, and before throwing, and deletion requires complete types in generaldeletion requires complete types in general

11Version 1.1 - January 18, 2008

Page 12: Shared Ptr

auto_ptrauto_ptr is inherently an enemy of the STL is inherently an enemy of the STL The STL loves ordinary value typesThe STL loves ordinary value types auto_ptrauto_ptr does not behave like an ordinary value does not behave like an ordinary value

typetype Whoever wins, we loseWhoever wins, we lose

shared_ptr is the STL's best friendshared_ptr is the STL's best friend shared_ptrshared_ptr behaves like an ordinary value type behaves like an ordinary value type In fact, In fact, shared_ptrshared_ptr wraps non-values like wraps non-values like

noncopyable and polymorphic types in value's noncopyable and polymorphic types in value's clothingclothing

vector<shared_ptr<Socket> >vector<shared_ptr<Socket> > vector<shared_ptr<Base> >vector<shared_ptr<Base> > Comes with Comes with operator<()operator<() for use in sets and maps for use in sets and maps

12Version 1.1 - January 18, 2008

Page 13: Shared Ptr

Policy CustomizablePolicy Customizable Loki smart pointers are extremely customizableLoki smart pointers are extremely customizable Ownership: refcount, reflink, destructive, etc.Ownership: refcount, reflink, destructive, etc. Implicit conversion to raw pointer: allow, disallowImplicit conversion to raw pointer: allow, disallow And so forthAnd so forth Policies are encoded in the smart pointer's type, Policies are encoded in the smart pointer's type,

preventing interoperability (sometimes, but not preventing interoperability (sometimes, but not always, solvable with ninja template heroics)always, solvable with ninja template heroics)

shared_ptrshared_ptr chooses good policies and bakes chooses good policies and bakes them inthem in

Deleters and allocators Deleters and allocators areare customizable, as they customizable, as they don't affect the typedon't affect the type

13Version 1.1 - January 18, 2008

Page 14: Shared Ptr

Containers of Containers of shared_ptrshared_ptr vector<shared_ptr<NoncopyableResource> >vector<shared_ptr<NoncopyableResource> > vector<shared_ptr<PolymorphicBase> >vector<shared_ptr<PolymorphicBase> > Any other STL/TR1 containers, especially caches:Any other STL/TR1 containers, especially caches: map<Key, shared_ptr<NoncopyableResource> >map<Key, shared_ptr<NoncopyableResource> >

Passing around copyable but "heavy" objects Passing around copyable but "heavy" objects efficiently (a simple version of move efficiently (a simple version of move semantics)semantics)

Superseding Superseding auto_ptrauto_ptr Holding dynamically allocated objects at local Holding dynamically allocated objects at local

scopescope Holding multiple dynamically allocated objects as Holding multiple dynamically allocated objects as

members (what does this mean? See next slide...)members (what does this mean? See next slide...)

14Version 1.1 - January 18, 2008

Page 15: Shared Ptr

Behold Behold LEAKTROCITYLEAKTROCITY::Foo::Foo() : m_p(0), m_q(0) {Foo::Foo() : m_p(0), m_q(0) {

m_p = new X;m_p = new X;

m_q = new Y;m_q = new Y;

}}

Foo::~Foo() {Foo::~Foo() {

delete m_p;delete m_p;

delete m_q;delete m_q;

}}

With With shared_ptrshared_ptr, this doesn't leak:, this doesn't leak:Foo::Foo() : m_sp(new X), m_sq(new Y) { }Foo::Foo() : m_sp(new X), m_sq(new Y) { }

// Implicitly defined dtor is OK for these members// Implicitly defined dtor is OK for these members

15Version 1.1 - January 18, 2008

Page 16: Shared Ptr

Guidelines:Guidelines: All occurrences of All occurrences of new[]new[]//delete[]delete[] should already should already

have been replaced with have been replaced with vectorvector All occurrences of All occurrences of newnew should immediately be given should immediately be given

to a named to a named shared_ptrshared_ptr All occurrences of All occurrences of deletedelete should vanish should vanish

Exceptions:Exceptions: When implementing custom data structures like When implementing custom data structures like

trees that can't be composed from the STL and TR1trees that can't be composed from the STL and TR1 When performance is absolutely criticalWhen performance is absolutely critical

Manual resource management is extremely Manual resource management is extremely difficult to do safely; consider it to be a last difficult to do safely; consider it to be a last resortresort

16Version 1.1 - January 18, 2008

Page 17: Shared Ptr

shared_ptr<string> sp(new string("meow"));shared_ptr<string> sp(new string("meow"));

cout << *sp << endl;cout << *sp << endl;

cout << sp->size() << endl;cout << sp->size() << endl;

Prints:Prints:meowmeow

44

Each Each newnew object is immediately given to a object is immediately given to a shared_ptrshared_ptr

Each Each deletedelete statement vanishes from statement vanishes from the sourcethe source

17Version 1.1 - January 18, 2008

Page 18: Shared Ptr

shared_ptr<string> sp shared_ptr<string> sp == new string("meow"); new string("meow");

Compiler error (after substitution):Compiler error (after substitution):error C2440: 'initializing' : cannot convert from error C2440: 'initializing' : cannot convert from

'std::string *' to 'std::tr1::shared_ptr<std::string>''std::string *' to 'std::tr1::shared_ptr<std::string>'

Constructor for class 'std::tr1::shared_ptr<std::string>' Constructor for class 'std::tr1::shared_ptr<std::string>' is declared 'explicit'is declared 'explicit'

Direct-initialization can use an explicit ctorDirect-initialization can use an explicit ctor Copy-initialization performs conversion: Copy-initialization performs conversion:

explicit ctors are unavailableexplicit ctors are unavailable shared_ptrshared_ptr acquires ownership explicitly acquires ownership explicitly

18Version 1.1 - January 18, 2008

Page 19: Shared Ptr

shared_ptr<int> a;shared_ptr<int> a;shared_ptr<int> b(new int(137));shared_ptr<int> b(new int(137));cout << (a ? "a" : "X") << endl;cout << (a ? "a" : "X") << endl;if (b) {if (b) { cout << "b" << endl;cout << "b" << endl;} else {} else { cout << "Y" << endl;cout << "Y" << endl;}} Prints:Prints:XXbb Also: Also: if (!sp)if (!sp), , if (sp && blah)if (sp && blah), , if (sp || blah)if (sp || blah)

19Version 1.1 - January 18, 2008

Page 20: Shared Ptr

auto_ptrauto_ptr: Not directly testable. Instead, : Not directly testable. Instead, you must test you must test if (ap.get())if (ap.get()) auto_ptrauto_ptr is deprecated in C++0x! is deprecated in C++0x!

unique_ptrunique_ptr: Has a conversion function to : Has a conversion function to unspecified-bool-typeunspecified-bool-type just like just like shared_ptrshared_ptr unique_ptrunique_ptr is the C++0x replacement for is the C++0x replacement for auto_ptrauto_ptr (not part of TR1) (not part of TR1)

weak_ptrweak_ptr: Not directly testable. Instead, : Not directly testable. Instead, you must test you must test if (!wp.expired())if (!wp.expired()) weak_ptrweak_ptr usage is covered later in this usage is covered later in this

presentationpresentation

20Version 1.1 - January 18, 2008

Page 21: Shared Ptr

shared_ptr<int> foo(int n) {shared_ptr<int> foo(int n) {

shared_ptr<int> r(new int(n));shared_ptr<int> r(new int(n));

*r += 5;*r += 5;

return r;return r;

}}

int main() {int main() {

shared_ptr<int> p = foo(3);shared_ptr<int> p = foo(3);

cout << *p << endl;cout << *p << endl;

}}

Prints:Prints:88

21Version 1.1 - January 18, 2008

Page 22: Shared Ptr

shared_ptr<int> a(new int(1));shared_ptr<int> a(new int(1));shared_ptr<int> b = a;shared_ptr<int> b = a;*a += 6;*a += 6;cout << *a << ", " << *b << endl;cout << *a << ", " << *b << endl;a.reset();a.reset();cout << "a: " << (a ? "owns" : "empty") << endl;cout << "a: " << (a ? "owns" : "empty") << endl;cout << "b: " << (b ? "owns" : "empty") << endl;cout << "b: " << (b ? "owns" : "empty") << endl;cout << *b << endl;cout << *b << endl; Prints:Prints:7, 77, 7a: emptya: emptyb: ownsb: owns77

22Version 1.1 - January 18, 2008

Page 23: Shared Ptr

shared_ptr<int> frob(new int(100));shared_ptr<int> frob(new int(100));

shared_ptr<const int> look = frob;shared_ptr<const int> look = frob;

cout << *look << endl;cout << *look << endl;

*frob /= 2;*frob /= 2;

cout << *look << endl;cout << *look << endl;

// *look /= 2;// *look /= 2;

Prints:Prints:100100

5050

Uncomment the last line to get this compiler Uncomment the last line to get this compiler error:error:

error C3892: 'look' : you cannot assign to a variable error C3892: 'look' : you cannot assign to a variable that is constthat is const

23Version 1.1 - January 18, 2008

Page 24: Shared Ptr

cats.txt:cats.txt:AbyssinianAbyssinianBalineseBalineseChesireChesireDevon RexDevon Rex dogs.txt:dogs.txt:AlsatianAlsatianBeagleBeagleCollieCollie people.txt:people.txt:AlanAlanBjarneBjarneCharlesCharlesDonaldDonaldEdsgerEdsger

24Version 1.1 - January 18, 2008

Page 25: Shared Ptr

queue<shared_ptr<ifstream> > q;queue<shared_ptr<ifstream> > q;

for (string s; getline(cin, s); ) {for (string s; getline(cin, s); ) { shared_ptr<ifstream> p(new ifstream(s.c_str()));shared_ptr<ifstream> p(new ifstream(s.c_str())); q.push(p);q.push(p);}}

while (!q.empty()) {while (!q.empty()) { string s;string s;

if (getline(*q.front(), s)) {if (getline(*q.front(), s)) { cout << s << endl;cout << s << endl; q.push(q.front());q.push(q.front()); }}

q.pop();q.pop();}}

25Version 1.1 - January 18, 2008

Page 26: Shared Ptr

cats.txtcats.txtdogs.txtdogs.txtpeople.txtpeople.txt^Z^ZAbyssinianAbyssinianAlsatianAlsatianAlanAlanBalineseBalineseBeagleBeagleBjarneBjarneChesireChesireCollieCollieCharlesCharlesDevon RexDevon RexDonaldDonaldEdsgerEdsger

26Version 1.1 - January 18, 2008

Page 27: Shared Ptr

class Animal {class Animal {

public:public:

explicit Animal(const string& name) : m_name(name) { }explicit Animal(const string& name) : m_name(name) { }

string noise() const {string noise() const {

return m_name + " says " + noise_impl();return m_name + " says " + noise_impl();

}}

virtual ~Animal() { }virtual ~Animal() { }

private:private:

Animal(const Animal&);Animal(const Animal&);

Animal& operator=(const Animal&);Animal& operator=(const Animal&);

virtual string noise_impl() const = 0;virtual string noise_impl() const = 0;

string m_name;string m_name;

};};

27Version 1.1 - January 18, 2008

Page 28: Shared Ptr

class Cat : public Animal {class Cat : public Animal {

public: explicit Cat(const string& name) : Animal(name) { }public: explicit Cat(const string& name) : Animal(name) { }

private: virtual string noise_impl() const { return "meow"; }private: virtual string noise_impl() const { return "meow"; }

};};

class Dog : public Animal {class Dog : public Animal {

public: explicit Dog(const string& name) : Animal(name) { }public: explicit Dog(const string& name) : Animal(name) { }

private: virtual string noise_impl() const { return "woof"; }private: virtual string noise_impl() const { return "woof"; }

};};

class Pig : public Animal {class Pig : public Animal {

public: explicit Pig(const string& name) : Animal(name) { }public: explicit Pig(const string& name) : Animal(name) { }

private: virtual string noise_impl() const { return "oink"; }private: virtual string noise_impl() const { return "oink"; }

};};

28Version 1.1 - January 18, 2008

Page 29: Shared Ptr

vector<shared_ptr<Animal> > v;vector<shared_ptr<Animal> > v;

shared_ptr<Cat> c(new Cat("Garfield"));shared_ptr<Cat> c(new Cat("Garfield"));

shared_ptr<Dog> d(new Dog("Odie"));shared_ptr<Dog> d(new Dog("Odie"));

shared_ptr<Pig> p(new Pig("Orson"));shared_ptr<Pig> p(new Pig("Orson"));

v.push_back(c);v.push_back(c);

v.push_back(d);v.push_back(d);

v.push_back(p);v.push_back(p);

transform(v.begin(), v.end(),transform(v.begin(), v.end(),

ostream_iterator<string>(cout, "\n"),ostream_iterator<string>(cout, "\n"),

mem_fn(&Animal::noise));mem_fn(&Animal::noise));

29Version 1.1 - January 18, 2008

Page 30: Shared Ptr

Garfield says meowGarfield says meow

Odie says woofOdie says woof

Orson says oinkOrson says oink

30Version 1.1 - January 18, 2008

Page 31: Shared Ptr

shared_ptr<Cat> p(new Cat("Peppermint"));shared_ptr<Cat> p(new Cat("Peppermint"));

shared_ptr<Cat> c;shared_ptr<Cat> c;

shared_ptr<Animal> a;shared_ptr<Animal> a;

c = p;c = p;

a = p;a = p;

cout << c->noise() << endl;cout << c->noise() << endl;

cout << a->noise() << endl;cout << a->noise() << endl;

Prints:Prints:Peppermint says meowPeppermint says meow

Peppermint says meowPeppermint says meow

31Version 1.1 - January 18, 2008

Page 32: Shared Ptr

shared_ptr<Cat> p(new Cat("Peppermint"));shared_ptr<Cat> p(new Cat("Peppermint"));

shared_ptr<Animal> a = p;shared_ptr<Animal> a = p;

cout << (p == a ? "same" : "different") << endl;cout << (p == a ? "same" : "different") << endl;

Prints:Prints:samesame

32Version 1.1 - January 18, 2008

Page 33: Shared Ptr

shared_ptr<Animal> a(new Cat("Bucky"));shared_ptr<Animal> a(new Cat("Bucky"));

cout << a->noise() << endl;cout << a->noise() << endl;

a.reset(new Dog("Satchel"));a.reset(new Dog("Satchel"));

cout << a->noise() << endl;cout << a->noise() << endl;

Prints:Prints:Bucky says meowBucky says meow

Satchel says woofSatchel says woof

33Version 1.1 - January 18, 2008

Page 34: Shared Ptr

shared_ptrshared_ptr has both member and free has both member and free swap()swap() Just like STL containersJust like STL containers

swap()swap() is intended to be implemented is intended to be implemented efficientlyefficiently In VC9 TR1, it is implemented efficientlyIn VC9 TR1, it is implemented efficiently "Efficient" means not modifying the refcounts"Efficient" means not modifying the refcounts

This is This is GOODGOOD::shared_ptr<string> a(new string("meow")); // meow: 1shared_ptr<string> a(new string("meow")); // meow: 1

shared_ptr<string> b(new string("purr")); // purr: 1shared_ptr<string> b(new string("purr")); // purr: 1

a.swap(b); // meow: 1, purr: 1a.swap(b); // meow: 1, purr: 1

swap(a, b); // meow: 1, purr: 1swap(a, b); // meow: 1, purr: 1

34Version 1.1 - January 18, 2008

Page 35: Shared Ptr

Behold Behold SLOWTROCITYSLOWTROCITY::shared_ptr<string> a(new string("meow")); // meow: 1shared_ptr<string> a(new string("meow")); // meow: 1shared_ptr<string> b(new string("purr")); // purr: 1shared_ptr<string> b(new string("purr")); // purr: 1{{ shared_ptr<string> t(a); // meow: 2 INCMEOWshared_ptr<string> t(a); // meow: 2 INCMEOW a = b; // meow: 1, purr: 2 DECMEOW, INCPURRa = b; // meow: 1, purr: 2 DECMEOW, INCPURR b = t; // meow: 2, purr: 1 INCMEOW, DECPURRb = t; // meow: 2, purr: 1 INCMEOW, DECPURR} // meow: 1 DECMEOW } // meow: 1 DECMEOW This unnecessarily modifies the refcounts 6 This unnecessarily modifies the refcounts 6

timestimes Even worse, this dereferences pointers 6 timesEven worse, this dereferences pointers 6 times Even worse, this uses interlocked operations 6 Even worse, this uses interlocked operations 6

timestimes Solution: Just use Solution: Just use swap()swap()

35Version 1.1 - January 18, 2008

Page 36: Shared Ptr

Correct:Correct:shared_ptr<int> owning(new int(47));shared_ptr<int> owning(new int(47));

int * raw = owning.get();int * raw = owning.get();

Incorrect:Incorrect:shared_ptr<int> owning(new int(47));shared_ptr<int> owning(new int(47));

int * raw = owning;int * raw = owning;

Compiler error (after substitution):Compiler error (after substitution):error C2440: 'initializing' : cannot convert from error C2440: 'initializing' : cannot convert from

'std::tr1::shared_ptr<int>' to 'int *''std::tr1::shared_ptr<int>' to 'int *'

No user-defined-conversion operator No user-defined-conversion operator available that can perform this conversion, or available that can perform this conversion, or the operator cannot be calledthe operator cannot be called

36Version 1.1 - January 18, 2008

Page 37: Shared Ptr

Which statements contain Which statements contain LEAKTROCITYLEAKTROCITY??f1(shared_ptr<Foo>(new Foo(args)));f1(shared_ptr<Foo>(new Foo(args)));

f2(shared_ptr<Foo>(new Foo(args)), g());f2(shared_ptr<Foo>(new Foo(args)), g());

f3(shared_ptr<Foo>(new Foo(args)),f3(shared_ptr<Foo>(new Foo(args)),

shared_ptr<Bar>(new Bar(args)));shared_ptr<Bar>(new Bar(args)));

Solution: Give each Solution: Give each shared_ptrshared_ptr a name a nameshared_ptr<Foo> foo(new Foo(args));shared_ptr<Foo> foo(new Foo(args));

shared_ptr<Bar> bar(new Bar(args));shared_ptr<Bar> bar(new Bar(args));

f1(foo);f1(foo);

f2(foo, g());f2(foo, g());

f3(foo, bar);f3(foo, bar);

37Version 1.1 - January 18, 2008

Page 38: Shared Ptr

void foo() {void foo() {

shared_ptr<int> sp(new int(1729));shared_ptr<int> sp(new int(1729));

int * raw = sp.get();int * raw = sp.get();

delete raw;delete raw;

}}

Result: Result: DOUBLE DELETIONDOUBLE DELETION Unlike Unlike auto_ptrauto_ptr, , shared_ptrshared_ptr has no has no release()release() member function member function

get()get() returns a non-owning raw pointer returns a non-owning raw pointer

38Version 1.1 - January 18, 2008

Page 39: Shared Ptr

struct Ansible {struct Ansible { shared_ptr<Ansible> get_shared() {shared_ptr<Ansible> get_shared() { shared_ptr<Ansible> ret(this);shared_ptr<Ansible> ret(this); return ret;return ret; }}};};

int main() {int main() { shared_ptr<Ansible> a(new Ansible);shared_ptr<Ansible> a(new Ansible); Ansible& r = *a;Ansible& r = *a; shared_ptr<Ansible> b = r.get_shared();shared_ptr<Ansible> b = r.get_shared();}} Result: Result: DOUBLE DELETIONDOUBLE DELETION

39Version 1.1 - January 18, 2008

Page 40: Shared Ptr

struct Ansiblestruct Ansible

: public enable_shared_from_this<Ansible> { };: public enable_shared_from_this<Ansible> { };

int main() {int main() {

shared_ptr<Ansible> a(new Ansible);shared_ptr<Ansible> a(new Ansible);

Ansible& r = *a;Ansible& r = *a;

shared_ptr<Ansible> b = r.shared_from_this();shared_ptr<Ansible> b = r.shared_from_this();

}}

aa and and bb share ownership, as if: share ownership, as if:shared_ptr<Ansible> b = a;shared_ptr<Ansible> b = a;

40Version 1.1 - January 18, 2008

Page 41: Shared Ptr

shared_ptr<int> a(new int(2161));shared_ptr<int> a(new int(2161));

shared_ptr<const int> b(a);shared_ptr<const int> b(a);

shared_ptr<int> c(const_cast<int *>(b.get()));shared_ptr<int> c(const_cast<int *>(b.get()));

Result: Result: DOUBLE DELETIONDOUBLE DELETION Solution: Use Solution: Use const_pointer_castconst_pointer_castshared_ptr<int> c(const_pointer_cast<int>(b));shared_ptr<int> c(const_pointer_cast<int>(b));

static_pointer_caststatic_pointer_cast, , dynamic_pointer_castdynamic_pointer_cast, and , and const_pointer_castconst_pointer_cast exist for exist for correctness, not conveniencecorrectness, not convenience

41Version 1.1 - January 18, 2008

Page 42: Shared Ptr

void observe(const weak_ptr<int>& wp) {void observe(const weak_ptr<int>& wp) { shared_ptr<int> t = wp.lock();shared_ptr<int> t = wp.lock(); cout << (t ? *t : 2010) << endl;cout << (t ? *t : 2010) << endl;}}

weak_ptr<int> wp;weak_ptr<int> wp;{{ shared_ptr<int> sp(new int(1969));shared_ptr<int> sp(new int(1969)); wp = sp;wp = sp; observe(wp);observe(wp);}}observe(wp);observe(wp); Prints:Prints:1969196920102010

42Version 1.1 - January 18, 2008

Page 43: Shared Ptr

Read: Any operation that can be performed to a Read: Any operation that can be performed to a const shared_ptrconst shared_ptr (copying, dereferencing, etc.) (copying, dereferencing, etc.)

Write: Any operation that cannot be performed to Write: Any operation that cannot be performed to a a const shared_ptrconst shared_ptr (assigning, resetting, (assigning, resetting, swapping, etc.)swapping, etc.)

Destruction counts as a writeDestruction counts as a write Multiple threads can simultaneously read a single Multiple threads can simultaneously read a single

shared_ptrshared_ptr object object Multiple threads can simultaneously read/write Multiple threads can simultaneously read/write

different different shared_ptrshared_ptr objects objects Even when the objects are copies that share ownershipEven when the objects are copies that share ownership

Anything else triggers Anything else triggers UNDEFINED BEHAVIORUNDEFINED BEHAVIOR Both VC9 TR1 and Boost provide these guaranteesBoth VC9 TR1 and Boost provide these guarantees

43Version 1.1 - January 18, 2008

Page 44: Shared Ptr

shared_ptrshared_ptr's ctor and 's ctor and reset()reset() can take can take an additional "deleter" argumentan additional "deleter" argument

A deleter is a functor that will be called A deleter is a functor that will be called with the stored raw pointer to release the with the stored raw pointer to release the owned objectowned object

Simplest example: Simplest example: free()free() The deleter's actual type is forgottenThe deleter's actual type is forgotten

As if through inheritanceAs if through inheritance The deleter stays with the owned objectThe deleter stays with the owned object

NOTNOT with the with the shared_ptrshared_ptr

44Version 1.1 - January 18, 2008

Page 45: Shared Ptr

Allocator support is a C++0x feature (not in TR1)Allocator support is a C++0x feature (not in TR1) Implemented by VC9 TR1 and Boost 1.35Implemented by VC9 TR1 and Boost 1.35

shared_ptr<T>shared_ptr<T> gains a three-arg ctor and gains a three-arg ctor and reset()reset() Taking Taking (T *, Deleter, Allocator)(T *, Deleter, Allocator)

The third argument:The third argument: Must be an STL allocator (20.1.5 lists the requirements)Must be an STL allocator (20.1.5 lists the requirements) Will be rebound (you can pass Will be rebound (you can pass YourAlloc<int>YourAlloc<int>)) Will be used to allocate/deallocate the reference countWill be used to allocate/deallocate the reference count

The allocator's actual type is forgottenThe allocator's actual type is forgotten As if through inheritanceAs if through inheritance

The allocator stays with the owned objectThe allocator stays with the owned object NOTNOT with the with the shared_ptrshared_ptr

45Version 1.1 - January 18, 2008

Page 46: Shared Ptr

shared_ptrshared_ptr and and weak_ptrweak_ptr contain two raw pointers: contain two raw pointers: Pointer to owned object (used for dereferencing)Pointer to owned object (used for dereferencing) Pointer to Pointer to _Ref_count_base_Ref_count_base

_Ref_count_base contains:_Ref_count_base contains: Pointer to owned object (used for deleting)Pointer to owned object (used for deleting) 32-bit strong refcount (# of 32-bit strong refcount (# of shared_ptrshared_ptrs)s) 32-bit weak refcount (# of 32-bit weak refcount (# of weak_ptrweak_ptrs + 1 for all s + 1 for all shared_ptrshared_ptrs)s)

When the strong refcount falls to zero:When the strong refcount falls to zero: _Ref_count_Ref_count deletedeletes the owned objects the owned object _Ref_count_d_Ref_count_d uses its stored deleter to nuke the owned object uses its stored deleter to nuke the owned object Both decrement the weak refcountBoth decrement the weak refcount

When the weak refcount falls to zero:When the weak refcount falls to zero: _Ref_count_Ref_count deletedeletes itselfs itself _Ref_count_d_Ref_count_d uses its stored allocator to nuke itself uses its stored allocator to nuke itself

Takeaways:Takeaways: shared_ptrshared_ptr is reasonably small is reasonably small Dereferencing a Dereferencing a shared_ptrshared_ptr involves involves ZERO OVERHEADZERO OVERHEAD

46Version 1.1 - January 18, 2008

Page 47: Shared Ptr

Destructors encapsulate resource releaseDestructors encapsulate resource release Destructors are resource agnosticDestructors are resource agnostic

Memory, files, sockets, locks, textures, etc.Memory, files, sockets, locks, textures, etc. Destructors are executed deterministicallyDestructors are executed deterministically STL containers enabled "one owning many"STL containers enabled "one owning many" shared_ptrshared_ptr enables "many owning one" enables "many owning one"

Object CategoryObject Category Owned By TheirOwned By Their Destroyed Destroyed WhenWhen

Automatic Block Control Leaves Block

Data Members Parent Parent Dies

Elements Container Container Dies

Dynamically Allocated

shared_ptrs All shared_ptrs Die

47Version 1.1 - January 18, 2008

Page 48: Shared Ptr

For more information, see:For more information, see: The TR1 draft: The TR1 draft: tinyurl.com/36lwqetinyurl.com/36lwqe The C++ Standard Library Extensions: A Tutorial The C++ Standard Library Extensions: A Tutorial

And ReferenceAnd Reference by Pete Becker: by Pete Becker: tinyurl.com/27jv8ntinyurl.com/27jv8n

Improving Improving shared_ptrshared_ptr For C++0x, Revision 2: For C++0x, Revision 2: tinyurl.com/2dlw3vtinyurl.com/2dlw3v Allocator Support, Aliasing Support, Object Creation, Allocator Support, Aliasing Support, Object Creation,

and Move Support were voted into the C++0x and Move Support were voted into the C++0x Working PaperWorking Paper

Improving Improving shared_ptrshared_ptr For C++0x, Revision 1: For C++0x, Revision 1: tinyurl.com/36cty7tinyurl.com/36cty7 Atomic Access and Cycle Collection are still plannedAtomic Access and Cycle Collection are still planned

48Version 1.1 - January 18, 2008