Reference counting smart pointer

Source: Internet
Author: User

Reference counting smart pointer
I. Introduction

Since the C ++ language does not have an automatic memory reclaim mechanism, the programmer must manually delete the new memory each time. When the programmer forgets to delete, the process is too complex and eventually leads to the absence of delete, exceptions lead to premature exit of the program, and the absence of delete execution is not uncommon. Using Smart pointers can effectively alleviate this type of problem. This article mainly describes the usage of smart pointers. These include std: auto_ptr, boost: scoped_ptr, boost: shared_ptr, boost: scoped_array, boost: shared_array, boost: weak_ptr, and boost: intrusive_ptr. You may think that so many smart pointers are used to solve the new and delete matching problems.

The following describes the seven smart pointers (smart_ptr) in sequence ).

II. Specific Use

For the compiler, the smart pointer is actually a stack object, not a pointer type. When the life cycle of the stack object is approaching, the smart pointer releases the heap memory that it manages through the destructor. All Smart pointers reload the operator-> operator to directly return references to objects for object operations. The "." operator is used to access the original smart pointer method.

You can use the get () function to access the bare pointer contained in the smart pointer. Because the smart pointer is an object, if (my_smart_object) is always true. To determine whether the bare pointer of the smart pointer is null, You need to determine if (my_smart_object.get ()).

The smart pointer contains the reset () method. If no parameter is passed (or NULL is passed), the smart pointer releases the memory currently managed. If an object is passed, the smart pointer releases the current object to manage the new object.

We compile a test class to assist in analysis:

Std: auto_ptr

Std: auto_ptr belongs to STL. Of course, the namespacestd contains the header file # include You can use it. Std: auto_ptr can easily manage a single heap memory object.

Std: auto_ptr source code, we can see that the culprit is "my_memory = my_memory". In this line of code, my_memory2 completely captures the Memory Management Ownership of my_memory, leading to the suspension of my_memory, it causes a crash.

Therefore, when std: auto_ptr is used, the "operator =" operator cannot be used.

We call the release () function to release the memory, which results in Memory leakage (in a memory-constrained system, if my_memory occupies too much memory, we will consider returning it immediately after use, instead of returning it after my_memory ends its life cycle ).

Originally, the std: auto_ptr release () function only gives out the memory ownership, which obviously does not conform to the C ++ programming philosophy.

Std: auto_ptr can be used to manage the pair memory of a single object. However, pay attention to the following points:

(1) Try not to use "operator = ". If yes, do not use the previous object.

(2) Remember that the release () function will not release the object and only return the ownership.

(3) std: auto_ptr should not be passed as a parameter (readers can write their own code to determine why they cannot ).

(4) due to the "operator =" problem of std: auto_ptr, objects managed by it cannot be placed in containers such as std: vector.

(5 )......

There are a lot of restrictions on using a std: auto_ptr. It cannot be used to manage heap memory arrays. This should be something you are thinking about now. I also think there are many restrictions, that day, an accident occurs.

Std: auto_ptr has caused many problems, and some designs are not very compatible with C ++ programming ideas.

Boost: scoped_ptr

Boost: scoped_ptr is a boost library and is defined in namespaceboost. It contains the header file # include You can use it. Like std: auto_ptr, boost: scoped_ptr can easily manage a single heap memory object. In particular, boost: scoped_ptr exclusive ownership, avoiding std :: auto_ptr annoying issues.

Boost: scoped_ptr can also be used normally like auto_ptr. But it does not have the release () function and won't cause previous Memory leakage problems. Secondly, because boost: scoped_ptr is exclusive to ownership, explicitly rejecting statements such as "my_memory2 = my_memory" can alleviate the annoying issue of std: auto_ptr.

Because boost: scoped_ptr exclusive ownership, when we really need to copy smart pointers, the demand will not be able to meet, so we will introduce another smart pointer for processing replication and parameter transfer, this is the following boost: shared_ptr.

 

Boost: shared_ptr

Boost: shared_ptr is a boost library and is defined in namespaceboost. It contains the header file # include You can use it. As we can see above, boost: scoped_ptr exclusive ownership, which cannot be assigned or copied. boost: shared_ptr is used to share ownership, it uses reference counting internally. Boost: shared_ptr is also used to manage a single heap memory object.

 

Boost: shared_ptr can also be easily used. There is no release () function. The key point is that boost: shared_ptr maintains a reference count internally, which supports replication and parameter transfer. Boost: shared_ptr provides a function use_count (), which returns the reference count in boost: shared_ptr. When we need to use a shared object, boost: shared_ptr is no better.

When shared_ptr is used in multiple threads, if the copy or assign value operation exists, the count may be invalid because the reference count is simultaneously accessed. The solution is to pass the public week_ptr to each thread. When shared_ptr is required in the thread, convert week_ptr to shared_ptr. You can use the following method to pass shared_ptr to another function:

Pass a value to shared_ptr. Call the copy constructor to increase the reference count and treat the called party as the owner. There is also a small amount of overhead in this operation, which depends largely on the number of shared_ptr objects you pass. This option is used when the Code Protocol (implicitly or explicitly) between the caller and the called party requires that the called party be the owner.

Shared_ptr is passed by reference or constant reference. In this case, the reference count does not increase, and as long as the caller does not exceed the range, the caller can access the pointer. Alternatively, the caller can decide to create a shared_ptr based on the reference to become a shared owner. Use this option when the caller does not know the called party, or when you must pass a shared_ptr and want to avoid replication due to performance reasons.

Use the underlying pointer or reference the underlying object. This allows the caller to use the object, but does not share ownership or extend the lifetime. If the caller creates a shared_ptr from the original pointer, the new shared_ptr is independent of the original one and does not control the underlying resources. This option is used when the agreements between the caller and the called specify that the caller retains the ownership of the shared_ptr lifetime.

When you decide how to pass a shared_ptr, determine whether the called party has the ownership to share the basic resource. An "owner" is the object or function of the underlying resource that can be used as long as it needs. If the caller must ensure that the caller can extend the lifetime of the pointer beyond its (function) lifetime, use the first option. If you do not care whether the called party has extended the lifetime, pass it by reference and let the called party copy it.

If you have to allow the help program function to access the underlying pointer, and you know that the help program function will use the pointer and return it before calling the function to return, the function does not have to share the ownership of the underlying pointer. Only allow access to pointers during the lifetime of the caller's shared_ptr. In this case, shared_ptr is passed through the reference, and the basic objects referenced through the original pointer or are safe. This method provides a small performance improvement and helps express the program's intent.

Sometimes, for example, in a std: vector >, You may have to pass each shared_ptr to the lambda expression body or name function object. If a lambda or function does not store pointers, The shared_ptr is passed through reference to avoid calling to copy every element of the constructor.

 

Boost: scoped_array

Boost: scoped_array is a boost library and is defined in namespaceboost. It contains the header file # include You can use it.

Boost: scoped_array is used to manage dynamic arrays. Like boost: scoped_ptr, it is also exclusive.

Boost: scoped_array My_memory (newSimple [2]); // use a memory array to initialize boost: scoped_array. The usage of boost: scoped_ptr is similar to that of boost: scoped_ptr. replication is not supported, in addition, dynamic arrays must be used during initialization. In addition, boost: scoped_array does not overload "operator *", but it does not matter. In general, we use the get () function more clearly.

Boost: shared_array

A smart pointer class that uses reference counting for copying and passing parameters.

Boost: shared_array is a boost library and is defined in namespaceboost. It contains the header file # include You can use it.

Because boost: scoped_array exclusive ownership, it is obvious that in many cases (parameter transfer, object assignment, etc.) does not meet the requirements, so we introduce boost: shared_array. Like boost: shared_ptr, reference count is used internally.

Like boost: shared_ptr, the reference count is used, which can be copied and passed through parameters.

So far, we have talked about smart pointers such as std: auto_ptr, boost: scoped_ptr, boost: shared_ptr, boost: scoped_array, and boost: shared_array. These smart pointers are enough for us to use, and 90% of the Code that uses standard smart pointers is five. There are two smart pointers as follows. They must be useful, but what is their use? Let's take a look.

 

Boost: weak_ptr

Boost: weak_ptr is a boost library and is defined in namespaceboost. It contains the header file # include You can use it.

Before talking about boost: weak_ptr, let's review the content described above. It seems that the boost: scoped_ptr and boost: shared_ptr smart pointers can be used to manage all the memory of a single object. There is also an extra boost: weak_ptr, are there still some situations that we haven't considered?

 

A: Yes. First, boost: weak_ptr is specially prepared for boost: shared_ptr. Sometimes, we only care about whether objects can be used, rather than the internal reference count. Boost: weak_ptr is The Observer (Observer) object of boost: shared_ptr. The Observer means that boost: weak_ptr only references boost: shared_ptr without changing its reference count, when the observed boost: shared_ptr fails, the corresponding boost: weak_ptr also fails.

Boost: weak_ptr My_memory_weak;

Boost: shared_ptr My_memory (newSimple (1 ))

We can see that, despite being assigned a value, the internal reference count remains unchanged. The question is, what is the role of boost: weak_ptr? From the example above, it seems that there is no function. In fact, boost: weak_ptr is mainly used in software architecture design and can be used in base classes (the base classes here are not abstract base classes, instead, it refers to the virtual base class inherited from the abstract base class) and defines a boost: weak_ptr, which is used to point to the boost: shared_ptr of the subclass, so that the base class only observes its own boost :: if weak_ptr is null, it means that the subclass has not assigned a value to itself, instead of affecting the reference count of the subclass boost: shared_ptr to reduce complexity and better manage objects.

Boost: intrusive_ptr

Boost: intrusive_ptr is a boost library and is defined in namespaceboost. It contains the header file # include You can use it.

After talking about the above six smart pointers, C ++ heap memory management is enough for general programs. Now there is a boost: intrusive_ptr, this is a plug-in smart pointer that does not contain reference counts. You need to add reference counts by the programmer. Otherwise, the compilation will fail. I personally think this smart pointer is not very useful, at least I have never used it. If you are interested, study the source code.

 

A self-built smart pointer with reference count: # include
 
  
Using namespace std; int const MEM_ALLOC = 100; class HeapTable {public: HeapTable () {mhead = new Node;} void AddRef (void * ptr) {Node * p = mhead-> mpnext; while (p! = NULL) {if (p-> mheapaddr = ptr) {p-> counter ++; return;} p = p-> mpnext ;} p = new Node (ptr); p-> mpnext = mhead-> mpnext; mhead-> mpnext = p;} void DelRef (void * ptr) {Node * p = mhead-> mpnext; while (p! = NULL) {if (p-> mheapaddr = ptr) {p-> counter --; return;} p = p-> mpnext ;}} int GetRef (void * ptr) {Node * p = mhead-> mpnext; while (p! = NULL) {if (p-> mheapaddr = ptr) {return p-> counter;} p = p-> mpnext;} return-1;} private: class Node {public: Node (void * ptr = NULL): mheapaddr (ptr), counter (0), mpnext (NULL) {if (mheapaddr! = NULL) // There is a new node {counter = 1 ;}} static void * operator new (size_t size); static void operator delete (void * ptr ); void * mheapaddr; int counter; Node * mpnext; static Node * mFreeList;}; Node * mhead ;}; HeapTable: Node * HeapTable: Node: mFreeList = NULL; void * HeapTable: Node: operator new (size_t size) {Node * p = NULL; if (mFreeList = NULL) {int allocsize = size * MEM_ALLOC; mFreeList = (Node *) new char [allocsize]; for (p = mFreeList; p
  
   
Mpnext = p + 1;} p-> mpnext = NULL;} p = mFreeList; mFreeList = mFreeList-> mpnext; // return p from the original node;} void HeapTable :: node: operator delete (void * ptr) {if (ptr = NULL) {return;} Node * p = (Node *) ptr; p-> mpnext = mFreeList; mFreeList = p;} template
   
    
Class CSmartPtr {public: CSmartPtr (T * ptr = NULL );~ CSmartPtr (); CSmartPtr (const CSmartPtr
    
     
& Src); CSmartPtr
     
      
& Operator = (const CSmartPtr
      
        & Src); T & operator * () {return * mptr;} void AddRef (); void DelRef (); int GetRef (); const T & operator *() const {return * mptr;} const T * operator-> () const {return mptr;} private: T * mptr; static HeapTable mHeapTable;}; template
       
         HeapTable CSmartPtr
        
          : MHeapTable; template
         
           CSmartPtr
          
            : CSmartPtr (T * ptr = NULL) {mptr = ptr; if (mptr! = NULL) {AddRef () ;}} template
           
             CSmartPtr
            
              : CSmartPtr (const CSmartPtr
             
               & Src) {mptr = src. mptr; if (mptr! = NULL) {AddRef () ;}} template
              
                CSmartPtr
               
                 & CSmartPtr
                
                  : Operator = (const CSmartPtr
                 
                   & Src) {if (this = & src) {return * this;} DelRef (); // subtract the reference count if (GetRef () = 0) {delete mptr; mptr = NULL;} AddRef (); mptr = src. mptr; return * this;} template
                  
                    CSmartPtr
                   
                     ::~ CSmartPtr () {DelRef (); if (GetRef () = 0) {delete mptr ;}} template
                    
                      Void CSmartPtr
                     
                       : AddRef () {mHeapTable. AddRef (mptr);} template
                      
                        Void CSmartPtr
                       
                         : DelRef () {mHeapTable. DelRef (mptr);} template
                        
                          Int CSmartPtr
                         
                           : GetRef () {return mHeapTable. GetRef (mptr);} class Text {public: Text () {cout <"construction call! "<
                          
                            Mptr (new Text); CSmartPtr
                           
                             Mptr2; mptr2 = mptr; CSmartPtr
                            
                              Mptr1 (new CHello); return 0 ;}
                            
                           
                          
                         
                        
                       
                      
                     
                    
                   
                  
                 
                
               
              
             
            
           
          
         
        
       
      
     
    
   
  
 


 

 

Summary

As mentioned above, it is necessary to summarize these smart pointers:

1. When the boost library can be used, std: auto_ptr is rejected because it not only does not conform to the C ++ programming idea, but also is prone to errors [2].

2. Use boost: scoped_ptr when determining that the object does not need to be shared (of course, boost: scoped_array is used for dynamic arrays ).

3. If the object needs to be shared, use boost: shared_ptr (of course, use boost: shared_array for dynamic arrays ).

4. When you need to access the boost: shared_ptr object without changing its reference count, use boost: weak_ptr, which is generally used in software framework design.

5. The last point is also the most demanding one: in your code, do not use the delete keyword (or C-Language free function), because it can be managed with a smart pointer.

 

 

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.