Boost---shared_ptr notes

Source: Internet
Author: User

 shared_ptr is a dynamic object that wraps the new operator on the heap to ensure that dynamically created objects can be deleted at all times, and that it implements a reference-counting smart pointer that can be freely copied and assigned, shared anywhere, It can be deleted when no code is used (reference count is 0 o'clock). 

SHARED_PTR can be safely placed in a standard container and make up for the defect that auto_ptr cannot use pointers as STL container elements because of the transfer semantics.


When do we need smart pointers?

There are three typical scenarios for using smart pointers:

    • Sharing of ownership of resources

    • To write code that is unusually secure

    • Avoid common errors, such as resource leaks

shared Ownership is the case where two or more objects need to use a third object at the same time. How (or when) should this third object be released? In order to ensure that the time to release is correct, each object that uses this shared resource must know each other to be able to accurately grasp the release time of the resource. From a design or maintenance standpoint, this coupling is not feasible. A better approach is to have these resource owners delegate the lifetime management responsibility of resources to a smart pointer. When no sharer exists, the smart pointer can safely release the resource.

exceptionally safe, simply to say that there is no resource leak when the exception is thrown and that the state of the program is consistent. If an object is dynamically allocated, it will not be deleted when the exception is thrown. Because the stack expands and the pointer leaves the scope, the resource can leak until the program ends (even if the resource collection at the end of the program is not guaranteed by the language). Not only can programs run out of resources due to memory leaks, but the state of the program can also become confusing. Smart pointers can automatically release these resources for you, even in the event of an exception.

avoid common mistakes. forgetting to call Delete is the oldest mistake in the book (at least in this one). A smart pointer does not care about the control path in the program; it only cares about removing it at the end of the lifetime of the object it points to. With smart pointers, you no longer need to know when to delete objects. Also, the smart pointer hides the details of freeing the resource, so the user does not need to know whether or not to call delete, and some special cleanup functions do not always delete the resource.

A safe and efficient smart pointer is an important weapon in the programmer's arsenal. Although std::auto_ptris available in the C + + standard library, it does not fully satisfy our need for smart pointers. For example,auto_ptr cannot be used as an element of an STL container. Boost's smart pointer class fills the gap left by the standard.


shared_ptr

Header file: "BOOST/SHARED_PTR.HPP"

shared_ptr can be constructed from a bare pointer, another shared_ptr, a std::auto_ptr, or a boost::weak_ptr . You can also pass the second argument to the constructor of shared_ptr , which is called a delete (deleter). The delete will be called later to handle the release of the shared resource. This is useful for managing resources that are not allocated with new and not freed with delete (see the example of creating a custom delete later). Once the shared_ptr is created, it can be used like a normal pointer, except that it cannot be explicitly deleted.

Partial summary of shared_ptr:

Namespace Boost {  Template<typename t> class shared_ptr {public:    template <class y> explicit shared_ PTR (y* p);    Template <class y,class d> shared_ptr (y* p,d D);    ~shared_ptr ();    shared_ptr (const shared_ptr & R);    Template <class y> Explicit       shared_ptr (const weak_ptr<y>& R);    Template <class y> Explicit shared_ptr (std::auto_ptr<y>& R);    shared_ptr& operator= (const shared_ptr& R);    void Reset ();       t& operator* () const;    t* operator-> () const;    t* get () const;    BOOL Unique () const;    Long Use_count () const;    operator Unspecified_bool_type () const;  The original is Unspecified-bool-type (), Error    void swap (shared_ptr<t>& b);  Template <class t,class u>       shared_ptr<t> static_pointer_cast (const shared_ptr<u>& r);}

member functions
Template <class y> Explicit shared_ptr (y* p);


This constructor gets the ownership of the given pointer p . The parameter p must be a valid pointer to Y . The post-construction reference count is set to 1. The only exception thrown from this constructor is Std::bad_alloc (which occurs only in a very rare case, that is, the free space required to reference the counter cannot be obtained).

Template <class y,class d> shared_ptr (y* p,d D);


This constructor has two parameters. The first is the resource that shared_ptr is going to take ownership of, and the second is an object that frees the resources when shared_ptr is destroyed, and the saved resources are passed to that object as D (p) . Therefore, whether the value of p is valid depends on D. If the reference counter cannot be assigned successfully,shared_ptr throws an exception of type Std::bad_alloc .

shared_ptr (const shared_ptr& R);


The resources saved in R are shared by the newly constructed shared_ptr , and the reference count is added one. This constructor does not throw an exception.

Template <class y> Explicit shared_ptr (const weak_ptr<y>& R);


Construct shared_ptr from a weak_ptr (described later in this chapter). This makes the use of weak_ptr thread-safe because the reference count of shared resources pointing to the weak_ptr parameter will be self-increasing (weak_ptr does not affect the reference count of shared resources). If weak_ptr is empty (r.use_count () ==0), shared_ptr throws an exception of type bad_weak_ptr .

Template <typename y> shared_ptr (std::auto_ptr<y>& R);


This constructor takes ownership of a pointer saved in R from a auto_ptr by saving a copy of the pointer and calling releaseto auto_ptr . The constructed reference count is 1. and R , of course, becomes empty. If the reference counter cannot be assigned successfully, the Std::bad_alloc is thrown.

~shared_ptr ();


shared_ptr destructors reduce the reference count by one. If the count is zero, the saved pointer is deleted. The way to delete a pointer is to call operator delete or, if given a custom-deleted object that performs a delete operation, invokes the saved pointer as a unique parameter. Destructors do not throw exceptions.

shared_ptr& operator= (const shared_ptr& R);  


The assignment operation shares resources in R and stops sharing the original resource. An assignment operation does not throw an exception.

void Reset ();


The reset function is used to stop sharing ownership of the saved pointer. The reference count of the shared resource is reduced by one.

t& operator* () const;


This operator returns a reference to the object to which the saved pointer is pointing. If the pointer is empty, calling operator* causes undefined behavior. This operator does not throw an exception.

t* operator-> () const;


This operator returns the saved pointer. This operator, together with operator* , makes the smart pointer look like a normal pointer. This operator does not throw an exception.

t* get () const;


The get function is the best way to get it when a saved pointer is likely to be empty (at which point both operator* and operator-> cause undefined behavior). Note that you can also use an implicit Boolean type conversion to test whether the shared_ptr contains a valid pointer. This function does not throw an exception.

BOOL Unique () const;


This function returns true if shared_ptr is the only owner of the pointer it holds, otherwise falseis returned. Unique does not throw an exception.

Long Use_count () const;      


The use_count function returns a reference count of pointers. It is especially useful when debugging because it can get a snapshot of the reference count at key points in the execution of the program. Use it carefully, because in some possible shared_ptr implementations, calculating the reference count can be expensive or even impossible. This function does not throw an exception.

operator Unspecified-bool-type () const;


This is an implicit conversion function to the unspecified-bool-type type, which can test a smart pointer in a Boolean context. If shared_ptr holds a valid pointer, the return value is True; otherwise false. Note that the type returned by the conversion function is indeterminate. Using the return type as bool can lead to some ridiculous operations, so the typical implementation uses safe bool idiom,[8] which ensures that only applicable Boolean tests can be used. This function does not throw an exception.


[8] invented by Peter Dimov.

void Swap (shared_ptr<t>& b);


This makes it easy to exchange two of shared_ptr. The swap function swaps the saved pointers (and their reference counts). This function does not throw an exception.


Common functions
Template <typename T,typename u>  


To execute static_caston a pointer held in shared_ptr , we can remove the pointer and cast it, but we cannot save it in another shared_ptr ; shared_ptr will consider it to be the first to manage these resources. The solution is to use static_pointer_cast. Use this function to ensure that the reference count of the referent remains correct. Static_pointer_cast does not throw an exception.

Usage

The main problem with shared_ptr is knowing the right time to delete a resource that is shared by multiple customers. Here is an easy-to-understand example, with two classes a and B, which share an int instance. With boost::shared_ptr, you need to include "BOOST/SHARED_PTR.HPP".

#include "boost/shared_ptr.hpp" #include <cassert>class a {  boost::shared_ptr<int> no_;public:  A (boost::shared_ptr<int> No): No_ (No) {}  void value (int i) {    *no_=i;  }}; Class B {  boost::shared_ptr<int> no_;public:  B (boost::shared_ptr<int> No): No_ (No) {}  int Value () const {    return *no_;  }}; int main () {    boost::shared_ptr<int> temp (new int);    A (temp);    b b (temp);    A.value (+);    ASSERT (B.value () ==28);}

Classes a and B all hold a shared_ptr<int>. When you create instances of a and B ,shared_ptr temp is routed to their constructors. This means a total of three shared_ptr:a,b, and temp, all of which lead to the same int instance. If we use pointers for a share,a and B must be able to indicate at some time that the int is to be deleted. In this example, until the end of main , the reference count is 3, when all shared_ptr leave the scope, the count will reach 0, and the last smart pointer will be responsible for deleting the shared int.

Review Pimpl (bridging mode) usage

The previous section shows the use of the Pimpl usage of scoped_ptr , and if the class using this usage is not allowed to replicate, then scoped_ptr works well when it saves the dynamic allocation instance of Pimpl. But this is not suitable for all types that want to benefit from PIMPL usage (note that you can also use scoped_ptr, but you must manually implement the copy constructor and assignment operators). For classes that can handle shared implementation details, you should use shared_ptr. When the ownership of Pimpl is passed to a shared_ptr, the copy and assignment operations are free. You can recall that when using scoped_ptr to handle the lifetime of the Pimpl class, replication of the encapsulated class is not allowed, because scoped_ptr is not replicable. This means that to enable these classes to support replication and assignment, you must manually define the copy constructor and assignment operators. When using shared_ptr to handle the lifetime of the Pimpl class, it is no longer necessary for the user to define the copy constructor themselves. Note that the Pimpl instance is shared by multiple objects of that class, so if the rule is that each Pimpl instance can only be used by one instance of the class, you still have to write the copy constructor manually. The solution is similar to what we saw in scoped_ptr , just to replace scoped_ptr with shared_ptr.

shared_ptrWith the standard library container

It is sometimes troublesome to put objects directly into the container. Saving an object as a value means that the consumer will get a copy of the elements in the container, which might be a performance issue for those types of expensive operations that replication is. In addition, some containers, especially std::vector, can replicate all elements when you add elements, which adds to the performance problem. Finally, the semantics of the value of the pass means that there is no polymorphic behavior. If you need to store polymorphic objects in a container and you don't want to cut them, you have to use pointers. If you use bare pointers, maintaining the integrity of the elements can be very complex. When you delete an element from a container, you must know whether the consumer of the container is still referencing the element that you want to delete, without worrying about multiple users using the same element. All these problems can be solved by shared_ptr .

There are two ways to apply shared_ptr to a standard container:

1: The container as a shared_ptr management object, such as shared_ptr<list<t>, so that the container can be safely shared, usage and ordinary shared_ptr no difference.

2: The shared_ptr as the element of the container, such as vector<shared_ptr<t>;.

Here's an example of how to put a shared pointer into a standard storage device.

#include "boost/shared_ptr.hpp" #include <vector> #include <iostream>class A {public:  virtual void Sing () =0;protected:  virtual ~a () {};}; Class B:public A {public:  virtual void Sing () {    std::cout << "does re mi fa so La";  }}; Boost::shared_ptr<a> Createa () {  boost::shared_ptr<a> P (new B ());  return p;} int main () {  typedef std::vector<boost::shared_ptr<a> > Container_type;  typedef container_type::iterator iterator;  Container_type container;  for (int i=0;i<10;++i) {    container.push_back (Createa ());  }  Std::cout << "The choir is gathered: \ n";  Iterator End=container.end ();  For (iterator It=container.begin (); it!=end;++it) {    (*it)->sing ();}  }

There are two classes, a and B, each with a virtual member function sing. b inherits from a public, and as you can see, the factory function Createa Returns an instance of the dynamically allocated B , wrapped in shared_ptr<a> . In main , a std::vector containing shared_ptr<a> is put into 10 elements, and the singis called at the end of each element. If we use bare pointers as elements, those objects need to be deleted manually. In this case, the deletion is automatic because the reference count for each shared_ptr is maintained at 1 for the lifetime of the vector , and when the vector is destroyed, all reference counters become 0. All objects are deleted. Interestingly, even if a 's destructor is not declared as virtual,shared_ptr will correctly invoke the destructor of B !

The example above demonstrates a powerful technique that involves a protected destructor in a . Because the function createa returns shared_ptr<a>, it is not possible to call delete on the pointer returned by Shared_ptr::get . This means that if you remove a bare pointer from a shared_ptr in order to send a bare pointer to a function that needs a bare pointer, it will not cause a disaster by accidentally being deleted. So, how do you allow shared_ptr to delete its objects? This is because the real type the pointer is pointing to IS B; And the destructor of B is not protected. This is a very useful method for adding additional security to objects in shared_ptr .

shared_ptrand other resources

Sometimes you will find that you want to use shared_ptr for a particular type, which requires other cleanup operations rather than simple delete. shared_ptr can be used to support this need through a custom-deleted device. Resources that handle operating system handles such as file* are typically freed using operations like fclose . To use file* in shared_ptr , we define a class to be responsible for releasing the appropriate resources.

Class Filecloser {public:   void operator () (file* FILE) {    std::cout << "The Filecloser have been called with a file*, "which'll now is      closed.\n";    if (file!=0)       fclose (file);}  ;

This is a function object, which we use to ensure that fclose is called when the resource is to be freed. The following is an example program that uses the Filecloser class.

int main () {  std::cout <<     "shared_ptr example with a custom deallocator.\n";   {    file* f=fopen ("Test.txt", "R");    if (f==0) {      std::cout << "Unable to open file\n";      Throw "Unable to open file";    }    Boost::shared_ptr<file>       My_shared_file (F, Filecloser ());    Locate the file pointer    fseek (My_shared_file.get (), 42,seek_set);  }  Std::cout << ' by ', the FILE has been closed!\n ";}

Note that when accessing resources, we need to use &* usage, get, or get_pointerfor shared_ptr . (Please note that it is best to use &*. The other two options are not very clear) This example can also be simpler, if we only need to invoke a single-argument function when releasing resources, there is no need to create a custom-type delete. The above example can be rewritten as follows:

{  file* f=fopen ("Test.txt", "R");  if (f==0) {    std::cout << "Unable to open file\n";    Throw File_exception ();  }    Boost::shared_ptr<file> My_shared_file (f,&fclose);  Locate the file pointer  fseek (&*my_shared_file,42,seek_set);} Std::cout << "By now, the file* have been closed!\n";

Custom deletes are useful when dealing with resources that require a special release program. Because the delete is not part of the shared_ptr type, the consumer does not need to know any information about the resources owned by the smart pointer (except, of course, how to use it!). )。 For example, you can use an object pool, and a custom delete simply returns the object to the pool. Or, a singleton object should use a delete that does nothing.

Security using a custom delete

We have seen the use of protected destructors on base classes to help increase the security of classes that use shared_ptr . Another way to achieve the same level of security is to declare the destructor to be protected (or private) and use a custom delete to destroy the object. This custom delete must be a friend of the class it is going to delete so that it can work. A good way to encapsulate this is to implement it as a private nested class, as shown in the following example:

#include "boost/shared_ptr.hpp" #include <iostream>class A {  class Deleter {public    :      void operator () (A * p) {        delete p;      }  };  Friend class Deleter;public:  virtual void Sing () {    std::cout << "Lalalalalalalalalalala";  }  Static boost::shared_ptr<a> Createa () {    boost::shared_ptr<a> P (New A (), A::d eleter ());    return p;  } Protected:  virtual ~a () {};}; int main () {  boost::shared_ptr<a> p=a::createa ();}

Note that we cannot use the normal function here as the factory function of shared_ptr<a> because the nested delete is a private. With this method, it is not possible for a user to create an object of a on the stack or to call delete on a pointer.

Create from thisshared_ptr

Sometimes you need to get shared_ptr from This , that is, you want your class to be managed by shared_ptr , you need to convert "self" to shared_ptr the method. Looks impossible? Well, the solution comes from another smart pointer we're about to discuss boost::weak_ptr .   weak_ptr is an observer of shared_ptr It just sits quietly and looks at them, but does not affect the reference count. By storing a weak_ptr that points to this as a member of the class, you can obtain a shared_ptr that points to this when needed. To save you from having to write code to hold a weak_ptr that points to this , and then get shared_ptr from weak_ptr , boost.smart_ptr Provides an helper class for this task, called Enable_shared_from_this . Simply make your class publicly derived from enable_shared_from_this , and then use the function when you need to access shared_ptr for administrative this shared_ From_this on the line. The following example demonstrates how to use enable_shared_from_this :

#include "boost/shared_ptr.hpp" #include "boost/enable_shared_from_this.hpp" Class A;void Do_stuff (boost::shared_ptr <A> p) {  ...} Class A:public boost::enable_shared_from_this<a> {public:  void Call_do_stuff () {    Do_stuff (shared_from _this ());}  ; int main () {  boost::shared_ptr<a> P (new A ());  P->call_do_stuff ();}

This example also demonstrates the situation in which you want to manage this with shared_ptr . Class A has a member function Call_do_stuff need to call a normal function Do_stuff, this ordinary function needs a type of boost:: Shared_ptr<a > 's parameters. Now, in A::call_do_stuff , this is just a pointer to a, but because a derives from enable_shared_from_ this, call Shared_from_this will return the shared_ptr we want. In the member function shared_from_this of Enable_shared_from_this , the internally stored weak_ptr is converted to Shared_ PTR, which increases the corresponding reference count to ensure that the corresponding object is not deleted.

Summarize

The reference count smart pointer is a very important tool. Boost's shared_ptr offers a robust and flexible solution that has been used in a wide variety of environments. It is common to need to share objects between users, and there is usually no way to tell the consumer when to delete the object is safe. shared_ptr allows users to not need to know about other objects that share objects, and lets them not worry about releasing resources when they don't have an object reference. This is most important for boost's smart pointer class. You'll see that there are other smart pointers in Boost.smart_ptr, but this one is definitely what you want most. By using a custom-made delete, almost all resource types can be deposited into shared_ptr. This makes shared_ptr a generic class for managing resource management, rather than just a dynamic allocation object. shared_ptr will have a little extra space cost compared to bare pointers. I have yet to find a situation where these costs are too great to find another solution. Do not create your own reference counting smart pointer class. Nothing is better than using shared_ptr smart pointers.

Use shared_ptr in the following situations:

    • When there are multiple users who use the same object without an obvious owner

    • When you want to put the pointer in a standard reservoir

    • When you want to transfer an object to a library or get an object from a library without explicit ownership

    • When managing some resources that require special cleanup methods


Factory functions

The shared_ptr construct also requires a new call, which results in some asymmetry in the code. Although shared_ptr is well-packaged with the new expression, it is also a problem to display the new operator too much, and it should be resolved using Factory mode.

SHARED_PTR provides a free factory function (located in the Boost namespace) make_shared<t> () in header file <boost/make_shared.hpp> to eliminate the display of the new call, Its name mimics the Make_pair () of the standard library and is declared as follows:

Template<class T,class ... args>    shared_ptr<t>make_shared (args &&. args);

The make_shared () function can accept up to 10 parameters and then pass them to the constructor of type T, creating an Shared_ptr<t> object and returning it. The make_shared () function is faster and more efficient than creating a shared_ptr object directly because it allocates only this memory internally, eliminating the overhead of shared_ptr constructs.

Instance:

#include <boost/make_sharde.hpp>int main () {    shared_ptr<string> sp = make_shared<string> ("Make _shared ");     shared_ptr<vector<int> > SPV = make_shared<vector<int> > (10,2);    ASSERT (Spv->size () ==10);}

Make_shared () cannot accept any number of parameters to construct an object, which in general does not become a problem. In fact, there are very few function interfaces with so many parameters, even if there is, that would be a good enough interface and should be refactored.

In addition to make_shared (), the SMART_PTR library also provides a allocate_shared (), which accepts a custom memory allocator type parameter more than make_shared (), and the other aspects are the same.




Boost---shared_ptr notes

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.