1//// 2Copyright 2003, 2017 Peter Dimov 3 4Distributed under the Boost Software License, Version 1.0. 5 6See accompanying file LICENSE_1_0.txt or copy at 7http://www.boost.org/LICENSE_1_0.txt 8//// 9 10[[techniques]] 11[appendix] 12# Smart Pointer Programming Techniques 13:toc: 14:toc-title: 15:idprefix: techniques_ 16 17[#techniques_incomplete] 18## Using incomplete classes for implementation hiding 19 20A proven technique (that works in C, too) for separating interface from implementation is to use a pointer to an incomplete class as an opaque handle: 21 22``` 23class FILE; 24 25FILE * fopen(char const * name, char const * mode); 26void fread(FILE * f, void * data, size_t size); 27void fclose(FILE * f); 28``` 29 30 31It is possible to express the above interface using `shared_ptr`, eliminating the need to manually call `fclose`: 32 33``` 34class FILE; 35 36shared_ptr<FILE> fopen(char const * name, char const * mode); 37void fread(shared_ptr<FILE> f, void * data, size_t size); 38``` 39 40This technique relies on `shared_ptr`’s ability to execute a custom deleter, eliminating the explicit call to `fclose`, and on the fact that `shared_ptr<X>` can be copied and destroyed when `X` is incomplete. 41 42## The "Pimpl" idiom 43 44A {cpp} specific variation of the incomplete class pattern is the "Pimpl" idiom. The incomplete class is not exposed to the user; it is hidden behind a forwarding facade. `shared_ptr` can be used to implement a "Pimpl": 45 46``` 47// file.hpp: 48 49class file 50{ 51private: 52 53 class impl; 54 shared_ptr<impl> pimpl_; 55 56public: 57 58 file(char const * name, char const * mode); 59 60 // compiler generated members are fine and useful 61 62 void read(void * data, size_t size); 63}; 64 65// file.cpp: 66 67#include "file.hpp" 68 69class file::impl 70{ 71private: 72 73 impl(impl const &); 74 impl & operator=(impl const &); 75 76 // private data 77 78public: 79 80 impl(char const * name, char const * mode) { ... } 81 ~impl() { ... } 82 void read(void * data, size_t size) { ... } 83}; 84 85file::file(char const * name, char const * mode): pimpl_(new impl(name, mode)) 86{ 87} 88 89void file::read(void * data, size_t size) 90{ 91 pimpl_->read(data, size); 92} 93``` 94 95The key thing to note here is that the compiler-generated copy constructor, assignment operator, and destructor all have a sensible meaning. As a result, `file` is `CopyConstructible` and `Assignable`, allowing its use in standard containers. 96 97## Using abstract classes for implementation hiding 98 99Another widely used C++ idiom for separating inteface and implementation is to use abstract base classes and factory functions. 100The abstract classes are sometimes called "interfaces" and the pattern is known as "interface-based programming". Again, 101`shared_ptr` can be used as the return type of the factory functions: 102 103``` 104// X.hpp: 105 106class X 107{ 108public: 109 110 virtual void f() = 0; 111 virtual void g() = 0; 112 113protected: 114 115 ~X() {} 116}; 117 118shared_ptr<X> createX(); 119 120// X.cpp: 121 122class X_impl: public X 123{ 124private: 125 126 X_impl(X_impl const &); 127 X_impl & operator=(X_impl const &); 128 129public: 130 131 virtual void f() 132 { 133 // ... 134 } 135 136 virtual void g() 137 { 138 // ... 139 } 140}; 141 142shared_ptr<X> createX() 143{ 144 shared_ptr<X> px(new X_impl); 145 return px; 146} 147``` 148 149A key property of `shared_ptr` is that the allocation, construction, deallocation, and destruction details are captured at the point of construction, inside the factory function. 150 151Note the protected and nonvirtual destructor in the example above. The client code cannot, and does not need to, delete a pointer to `X`; the `shared_ptr<X>` instance returned from `createX` will correctly call `~X_impl`. 152 153## Preventing `delete px.get()` 154 155It is often desirable to prevent client code from deleting a pointer that is being managed by `shared_ptr`. The previous technique showed one possible approach, using a protected destructor. Another alternative is to use a private deleter: 156 157``` 158class X 159{ 160private: 161 162 ~X(); 163 164 class deleter; 165 friend class deleter; 166 167 class deleter 168 { 169 public: 170 171 void operator()(X * p) { delete p; } 172 }; 173 174public: 175 176 static shared_ptr<X> create() 177 { 178 shared_ptr<X> px(new X, X::deleter()); 179 return px; 180 } 181}; 182``` 183 184## Encapsulating allocation details, wrapping factory functions 185 186`shared_ptr` can be used in creating {cpp} wrappers over existing C style library interfaces that return raw pointers from their factory functions 187to encapsulate allocation details. As an example, consider this interface, where `CreateX` might allocate `X` from its own private heap, `~X` may 188be inaccessible, or `X` may be incomplete: 189 190 X * CreateX(); 191 void DestroyX(X *); 192 193The only way to reliably destroy a pointer returned by `CreateX` is to call `DestroyX`. 194 195Here is how a `shared_ptr`-based wrapper may look like: 196 197 shared_ptr<X> createX() 198 { 199 shared_ptr<X> px(CreateX(), DestroyX); 200 return px; 201 } 202 203Client code that calls `createX` still does not need to know how the object has been allocated, but now the destruction is automatic. 204 205[#techniques_static] 206## Using a shared_ptr to hold a pointer to a statically allocated object 207 208Sometimes it is desirable to create a `shared_ptr` to an already existing object, so that the `shared_ptr` does not attempt to destroy the 209object when there are no more references left. As an example, the factory function: 210 211 shared_ptr<X> createX(); 212 213in certain situations may need to return a pointer to a statically allocated `X` instance. 214 215The solution is to use a custom deleter that does nothing: 216 217``` 218struct null_deleter 219{ 220 void operator()(void const *) const 221 { 222 } 223}; 224 225static X x; 226 227shared_ptr<X> createX() 228{ 229 shared_ptr<X> px(&x, null_deleter()); 230 return px; 231} 232``` 233 234The same technique works for any object known to outlive the pointer. 235 236## Using a shared_ptr to hold a pointer to a COM Object 237 238Background: COM objects have an embedded reference count and two member functions that manipulate it. `AddRef()` increments the count. 239`Release()` decrements the count and destroys itself when the count drops to zero. 240 241It is possible to hold a pointer to a COM object in a `shared_ptr`: 242 243 shared_ptr<IWhatever> make_shared_from_COM(IWhatever * p) 244 { 245 p->AddRef(); 246 shared_ptr<IWhatever> pw(p, mem_fn(&IWhatever::Release)); 247 return pw; 248 } 249 250Note, however, that `shared_ptr` copies created from `pw` will not "register" in the embedded count of the COM object; 251they will share the single reference created in `make_shared_from_COM`. Weak pointers created from `pw` will be invalidated when the last 252`shared_ptr` is destroyed, regardless of whether the COM object itself is still alive. 253 254As link:../../../../libs/bind/mem_fn.html#Q3[explained] in the `mem_fn` documentation, you need to `#define BOOST_MEM_FN_ENABLE_STDCALL` first. 255 256[#techniques_intrusive] 257## Using a shared_ptr to hold a pointer to an object with an embedded reference count 258 259This is a generalization of the above technique. The example assumes that the object implements the two functions required by `<<intrusive_ptr,intrusive_ptr>>`, 260`intrusive_ptr_add_ref` and `intrusive_ptr_release`: 261 262``` 263template<class T> struct intrusive_deleter 264{ 265 void operator()(T * p) 266 { 267 if(p) intrusive_ptr_release(p); 268 } 269}; 270 271shared_ptr<X> make_shared_from_intrusive(X * p) 272{ 273 if(p) intrusive_ptr_add_ref(p); 274 shared_ptr<X> px(p, intrusive_deleter<X>()); 275 return px; 276} 277``` 278 279## Using a shared_ptr to hold another shared ownership smart pointer 280 281One of the design goals of `shared_ptr` is to be used in library interfaces. It is possible to encounter a situation where a library takes a 282`shared_ptr` argument, but the object at hand is being managed by a different reference counted or linked smart pointer. 283 284It is possible to exploit `shared_ptr`’s custom deleter feature to wrap this existing smart pointer behind a `shared_ptr` facade: 285 286``` 287template<class P> struct smart_pointer_deleter 288{ 289private: 290 291 P p_; 292 293public: 294 295 smart_pointer_deleter(P const & p): p_(p) 296 { 297 } 298 299 void operator()(void const *) 300 { 301 p_.reset(); 302 } 303 304 P const & get() const 305 { 306 return p_; 307 } 308}; 309 310shared_ptr<X> make_shared_from_another(another_ptr<X> qx) 311{ 312 shared_ptr<X> px(qx.get(), smart_pointer_deleter< another_ptr<X> >(qx)); 313 return px; 314} 315``` 316 317One subtle point is that deleters are not allowed to throw exceptions, and the above example as written assumes that `p_.reset()` doesn't throw. 318If this is not the case, `p_.reset();` should be wrapped in a `try {} catch(...) {}` block that ignores exceptions. In the (usually unlikely) event 319when an exception is thrown and ignored, `p_` will be released when the lifetime of the deleter ends. This happens when all references, including 320weak pointers, are destroyed or reset. 321 322Another twist is that it is possible, given the above `shared_ptr` instance, to recover the original smart pointer, using `<<shared_ptr_get_deleter,get_deleter>>`: 323 324``` 325void extract_another_from_shared(shared_ptr<X> px) 326{ 327 typedef smart_pointer_deleter< another_ptr<X> > deleter; 328 329 if(deleter const * pd = get_deleter<deleter>(px)) 330 { 331 another_ptr<X> qx = pd->get(); 332 } 333 else 334 { 335 // not one of ours 336 } 337} 338``` 339 340[#techniques_from_raw] 341## Obtaining a shared_ptr from a raw pointer 342 343Sometimes it is necessary to obtain a `shared_ptr` given a raw pointer to an object that is already managed by another `shared_ptr` instance. Example: 344 345 void f(X * p) 346 { 347 shared_ptr<X> px(???); 348 } 349 350Inside `f`, we'd like to create a `shared_ptr` to `*p`. 351 352In the general case, this problem has no solution. One approach is to modify `f` to take a `shared_ptr`, if possible: 353 354 void f(shared_ptr<X> px); 355 356The same transformation can be used for nonvirtual member functions, to convert the implicit `this`: 357 358 void X::f(int m); 359 360would become a free function with a `shared_ptr` first argument: 361 362 void f(shared_ptr<X> this_, int m); 363 364If `f` cannot be changed, but `X` uses intrusive counting, use `<<techniques_intrusive,make_shared_from_intrusive>>` described above. Or, if it's known that the `shared_ptr` created in `f` will never outlive the object, use <<techniques_static,a null deleter>>. 365 366## Obtaining a shared_ptr (weak_ptr) to this in a constructor 367 368Some designs require objects to register themselves on construction with a central authority. When the registration routines take a `shared_ptr`, this leads to the question how could a constructor obtain a `shared_ptr` to `this`: 369 370``` 371class X 372{ 373public: 374 375 X() 376 { 377 shared_ptr<X> this_(???); 378 } 379}; 380``` 381 382In the general case, the problem cannot be solved. The `X` instance being constructed can be an automatic variable or a static variable; it can be created on the heap: 383 384 shared_ptr<X> px(new X); 385 386but at construction time, `px` does not exist yet, and it is impossible to create another `shared_ptr` instance that shares ownership with it. 387 388Depending on context, if the inner `shared_ptr this_` doesn't need to keep the object alive, use a `null_deleter` as explained <<techniques_static,here>> and <<techniques_weak_without_shared,here>>. 389If `X` is supposed to always live on the heap, and be managed by a `shared_ptr`, use a static factory function: 390 391``` 392class X 393{ 394private: 395 396 X() { ... } 397 398public: 399 400 static shared_ptr<X> create() 401 { 402 shared_ptr<X> px(new X); 403 // use px as 'this_' 404 return px; 405 } 406}; 407``` 408 409## Obtaining a shared_ptr to this 410 411Sometimes it is needed to obtain a `shared_ptr` from `this` in a virtual member function under the assumption that `this` is already managed by a `shared_ptr`. 412The transformations <<techniques_from_raw,described in the previous technique>> cannot be applied. 413 414A typical example: 415 416``` 417class X 418{ 419public: 420 421 virtual void f() = 0; 422 423protected: 424 425 ~X() {} 426}; 427 428class Y 429{ 430public: 431 432 virtual shared_ptr<X> getX() = 0; 433 434protected: 435 436 ~Y() {} 437}; 438 439// -- 440 441class impl: public X, public Y 442{ 443public: 444 445 impl() { ... } 446 447 virtual void f() { ... } 448 449 virtual shared_ptr<X> getX() 450 { 451 shared_ptr<X> px(???); 452 return px; 453 } 454}; 455``` 456 457The solution is to keep a weak pointer to `this` as a member in `impl`: 458 459``` 460class impl: public X, public Y 461{ 462private: 463 464 weak_ptr<impl> weak_this; 465 466 impl(impl const &); 467 impl & operator=(impl const &); 468 469 impl() { ... } 470 471public: 472 473 static shared_ptr<impl> create() 474 { 475 shared_ptr<impl> pi(new impl); 476 pi->weak_this = pi; 477 return pi; 478 } 479 480 virtual void f() { ... } 481 482 virtual shared_ptr<X> getX() 483 { 484 shared_ptr<X> px(weak_this); 485 return px; 486 } 487}; 488``` 489 490The library now includes a helper class template `<<enable_shared_from_this,enable_shared_from_this>>` that can be used to encapsulate the solution: 491 492``` 493class impl: public X, public Y, public enable_shared_from_this<impl> 494{ 495public: 496 497 impl(impl const &); 498 impl & operator=(impl const &); 499 500public: 501 502 virtual void f() { ... } 503 504 virtual shared_ptr<X> getX() 505 { 506 return shared_from_this(); 507 } 508} 509``` 510 511Note that you no longer need to manually initialize the `weak_ptr` member in `enable_shared_from_this`. Constructing a `shared_ptr` to `impl` takes care of that. 512 513## Using shared_ptr as a smart counted handle 514 515Some library interfaces use opaque handles, a variation of the <<techniques_incomplete,incomplete class technique>> described above. An example: 516 517``` 518typedef void * HANDLE; 519 520HANDLE CreateProcess(); 521void CloseHandle(HANDLE); 522``` 523 524Instead of a raw pointer, it is possible to use `shared_ptr` as the handle and get reference counting and automatic resource management for free: 525 526``` 527typedef shared_ptr<void> handle; 528 529handle createProcess() 530{ 531 shared_ptr<void> pv(CreateProcess(), CloseHandle); 532 return pv; 533} 534``` 535 536## Using shared_ptr to execute code on block exit 537 538`shared_ptr<void>` can automatically execute cleanup code when control leaves a scope. 539 540* Executing `f(p)`, where `p` is a pointer: 541+ 542``` 543shared_ptr<void> guard(p, f); 544``` 545 546* Executing arbitrary code: `f(x, y)`: 547+ 548``` 549shared_ptr<void> guard(static_cast<void*>(0), bind(f, x, y)); 550``` 551 552## Using shared_ptr<void> to hold an arbitrary object 553 554`shared_ptr<void>` can act as a generic object pointer similar to `void*`. When a `shared_ptr<void>` instance constructed as: 555 556 shared_ptr<void> pv(new X); 557 558is destroyed, it will correctly dispose of the `X` object by executing `~X`. 559 560This propery can be used in much the same manner as a raw `void*` is used to temporarily strip type information from an object pointer. 561A `shared_ptr<void>` can later be cast back to the correct type by using `<<shared_ptr_static_pointer_cast,static_pointer_cast>>`. 562 563## Associating arbitrary data with heterogeneous `shared_ptr` instances 564 565`shared_ptr` and `weak_ptr` support `operator<` comparisons required by standard associative containers such as `std::map`. This can be 566used to non-intrusively associate arbitrary data with objects managed by `shared_ptr`: 567 568``` 569typedef int Data; 570 571std::map<shared_ptr<void>, Data> userData; 572// or std::map<weak_ptr<void>, Data> userData; to not affect the lifetime 573 574shared_ptr<X> px(new X); 575shared_ptr<int> pi(new int(3)); 576 577userData[px] = 42; 578userData[pi] = 91; 579``` 580 581## Using `shared_ptr` as a `CopyConstructible` mutex lock 582 583Sometimes it's necessary to return a mutex lock from a function, and a noncopyable lock cannot be returned by value. It is possible to use `shared_ptr` as a mutex lock: 584 585``` 586class mutex 587{ 588public: 589 590 void lock(); 591 void unlock(); 592}; 593 594shared_ptr<mutex> lock(mutex & m) 595{ 596 m.lock(); 597 return shared_ptr<mutex>(&m, mem_fn(&mutex::unlock)); 598} 599``` 600 601Better yet, the `shared_ptr` instance acting as a lock can be encapsulated in a dedicated `shared_lock` class: 602 603``` 604class shared_lock 605{ 606private: 607 608 shared_ptr<void> pv; 609 610public: 611 612 template<class Mutex> explicit shared_lock(Mutex & m): pv((m.lock(), &m), mem_fn(&Mutex::unlock)) {} 613}; 614``` 615 616`shared_lock` can now be used as: 617 618 shared_lock lock(m); 619 620Note that `shared_lock` is not templated on the mutex type, thanks to `shared_ptr<void>`’s ability to hide type information. 621 622## Using shared_ptr to wrap member function calls 623 624`shared_ptr` implements the ownership semantics required from the `Wrap/CallProxy` scheme described in Bjarne Stroustrup's article 625"Wrapping C++ Member Function Calls" (available online at http://www.stroustrup.com/wrapper.pdf). An implementation is given below: 626 627``` 628template<class T> class pointer 629{ 630private: 631 632 T * p_; 633 634public: 635 636 explicit pointer(T * p): p_(p) 637 { 638 } 639 640 shared_ptr<T> operator->() const 641 { 642 p_->prefix(); 643 return shared_ptr<T>(p_, mem_fn(&T::suffix)); 644 } 645}; 646 647class X 648{ 649private: 650 651 void prefix(); 652 void suffix(); 653 friend class pointer<X>; 654 655public: 656 657 void f(); 658 void g(); 659}; 660 661int main() 662{ 663 X x; 664 665 pointer<X> px(&x); 666 667 px->f(); 668 px->g(); 669} 670``` 671 672## Delayed deallocation 673 674In some situations, a single `px.reset()` can trigger an expensive deallocation in a performance-critical region: 675 676``` 677class X; // ~X is expensive 678 679class Y 680{ 681 shared_ptr<X> px; 682 683public: 684 685 void f() 686 { 687 px.reset(); 688 } 689}; 690``` 691 692The solution is to postpone the potential deallocation by moving `px` to a dedicated free list that can be periodically emptied when performance and response times are not an issue: 693 694``` 695vector< shared_ptr<void> > free_list; 696 697class Y 698{ 699 shared_ptr<X> px; 700 701public: 702 703 void f() 704 { 705 free_list.push_back(px); 706 px.reset(); 707 } 708}; 709 710// periodically invoke free_list.clear() when convenient 711``` 712 713Another variation is to move the free list logic to the construction point by using a delayed deleter: 714 715``` 716struct delayed_deleter 717{ 718 template<class T> void operator()(T * p) 719 { 720 try 721 { 722 shared_ptr<void> pv(p); 723 free_list.push_back(pv); 724 } 725 catch(...) 726 { 727 } 728 } 729}; 730``` 731 732[#techniques_weak_without_shared] 733## Weak pointers to objects not managed by a shared_ptr 734 735Make the object hold a `shared_ptr` to itself, using a `null_deleter`: 736 737``` 738class X 739{ 740private: 741 742 shared_ptr<X> this_; 743 int i_; 744 745public: 746 747 explicit X(int i): this_(this, null_deleter()), i_(i) 748 { 749 } 750 751 // repeat in all constructors (including the copy constructor!) 752 753 X(X const & rhs): this_(this, null_deleter()), i_(rhs.i_) 754 { 755 } 756 757 // do not forget to not assign this_ in the copy assignment 758 759 X & operator=(X const & rhs) 760 { 761 i_ = rhs.i_; 762 } 763 764 weak_ptr<X> get_weak_ptr() const { return this_; } 765}; 766``` 767 768When the object's lifetime ends, `X::this_` will be destroyed, and all weak pointers will automatically expire. 769