Intelligent pointers: auto_ptr, scoped_ptr, shared_ptr, weak_ptr, and auto_ptrscoped_ptr

Source: Internet
Author: User

Intelligent pointers: auto_ptr, scoped_ptr, shared_ptr, weak_ptr, and auto_ptrscoped_ptr
 What is RAII?

RAII Is short for Resource Acquisition Is Initialization. It Is a common usage of C ++ to manage resources and avoid leakage.

RAII is also called resource allocation and initialization. It defines a class to encapsulate resource allocation and release. After the constructor completes resource allocation and initialization, the constructor cleans up resources, ensure that resources are correctly initialized and released.

Why RAII? In computer systems, resources are a limited number of elements that play a certain role in the normal operation of the system. For example, network sockets, mutex locks, file handles, and memory are system resources. Because system resources are limited, we must follow one step when programming to use system resources:

Steps 1 and 3 are indispensable because resources must be applied for before they can be used. After they are used, they must be released. If they are not released, resource leakage may occur.

What is smart pointer?

 Smart pointers are the release of Dynamic resources that smart/automated management pointers point. It is a class and has a function similar to pointer.

How does smart pointer work? When a class contains pointer members, there are two methods to manage pointer members:
  • First, management is implemented in a value-type manner. Each Class Object retains a copy of the object pointed to by the pointer;
  • Another more elegant way is to use smart pointers to share the objects pointed to by pointers.
Smart pointer is a general implementation technique that uses reference count ). The smart pointer class associates a counter with the object to which the class points. The reference counting class traces how many objects in the class pointer to the same object.

Each time you create a new object of the class, the initialization pointer sets the reference count to 1. When the object is created as a copy of another object, copy the constructor to copy the pointer and add the corresponding reference count. when an object is assigned a value, the value assignment operator reduces the reference count of the object indicated by the left operand (if the reference count is reduced to 0, delete the object), and increase the reference count of the object indicated by the right operand. when calling the destructor, The Destructor reduces the reference count (if the reference count is reduced to 0, the basic object is deleted ).

Common smart pointers

Including:Std: auto_ptr, Boost: scoped_ptr, boost: shared_ptr, boost: scoped_array,Boost: shared_array,Boost: weak_ptr, boost ::Intrusive_ptr

Boost library smart pointer (ps: the new C ++ 11 standard has introduced unique_ptr/shared_ptr/weak_ptr ):

 Auto_ptr exclusive ownership, transfer ownership

 First implementation: In the beginning, the auto_ptr member variables mainly include T * _ ptr and bool _ owner. The main implementation principle is to assign the owner of the management space to the object during object construction, transfers the ownership of a space in the copy or assign value. In the destructor, when _ owner is true (with ownership), the ownership is released.

Template <typename T> class AutoPtr {public: // constructor explicit AutoPtr (T * ptr = NULL): _ ptr (ptr), _ owner (true) {} // copy and construct AutoPtr (AutoPtr <T> & ap) // The parameter cannot be written as const. Modify the members of the ap object here.
: _ Ptr (ap. _ ptr), _ owner (true) {ap. _ owner = false; // transfer permission} // The Value assignment operator reloads AutoPtr & operator = (AutoPtr <T> & ap) {if (this! = & Ap) {delete this-> _ ptr; this-> _ ptr = ap. _ ptr; // transfer permission this-> _ owner = true; ap. _ owner = false;} return * this;} // destructor ~ AutoPtr () {// only delete the pointer with permissions if (_ owner) {this-> _ owner = false; delete this-> _ ptr ;}} T & operator * () {return * (this-> _ ptr);} T * operator-> () {return this-> _ ptr ;} T * AutoPtr <T >:: GetStr () {return (this-> _ ptr);} protected: T * _ ptr; bool _ owner; // permission owner };

Main problems:If the Copied object is out of scope or calls the Destructor before the original objectThe _ owner of the original object, although false, is accessing a space that has been released, the reason is that the release of the copy object will cause the content pointed to by the _ ptr of the original object to be released!

The problem is as follows:

Ap1 grants the Destructor permission to ap2. Because ap1. _ ptr and ap2. _ ptr point to the same space, ap2 is automatically released after the if scope is exceeded, in this case, ap1. _ ptr is also released.

However, access to ap1. _ ptr outside the scope of if causes the program to crash. At this time, ap1. _ ptr is already a wild pointer, which causesPointer SuspensionProblem!

Void Test () {AutoPtr <int> ap1 (new int (1); if (true) {AutoPtr <int> ap2 (ap1 );} // here ap1. _ ptr is already a wild pointer, which causes the pointer suspension problem * (ap1.GetStr () = 10 ;}

Auto_ptrSecond ImplementationMethod: it is still the ownership transfer of the management space, but this implementation method does not have the _ owner permission owner. The constructor is similar to the above implementation method. However, after copying and assigning values, _ ptr is directly assigned as null, And it is relatively simple and crude to prohibit another access to the original memory space.

Template <typename T> class AutoPtr {public: // constructor explicit AutoPtr (T * ptr = NULL) // It cannot be written as const T * ptr, otherwise, the const type is assigned to a non-const type: _ ptr (ptr) {}// copy to construct AutoPtr (AutoPtr <T> & ap): _ ptr (ap. _ ptr) {ap. _ ptr = NULL;} // The Value assignment operator reloads AutoPtr & operator = (AutoPtr <T> & ap) {if (this! = & Ap) {delete this-> _ ptr; this-> _ ptr = ap. _ ptr; ap. _ ptr = NULL;} return * this;} // destructor ~ AutoPtr () {// only delete the pointer with the permission if (_ ptr) {delete _ ptr;} T & operator * () {return * _ ptr ;} T * operator-> () {return _ ptr;} T * GetStr () {return _ ptr;} protected: T * _ ptr ;};

This method solves the old version of the wild pointer, but because it implements full permission transfer, only one pointer can be used after the copy structure and assignment, other pointers are set to NULL, which is inconvenient to use. It is also easy to dereference the NULL pointer, causing program crash, which causes great harm.

 Scoped_ptr exclusive ownership, anti-copy

Scoped_ptr is implemented to prevent copying and assigning values between objects. The specific implementation is to set the copy constructor and the value assignment operator overload function to be protected or private, and only declare not implemented, and set the flag to be protected or private to prevent others from copying outside the class, simple and crude, but it also improves the code security.

Template <typename T> class ScopedPtr {public: ScopedPtr (T * ptr = NULL): _ ptr (ptr) {}t & operator * () {return * _ ptr ;} T * operator-> () {return _ ptr;} T * GetStr () {return _ ptr;} // destructor ~ ScopedPtr () {if (_ ptr! = NULL) {delete _ ptr ;}} protected: // prevents copy ScopedPtr (ScopedPtr <T> & ap); ScopedPtr & operator = (ScopedPtr <T> & ap ); T * _ ptr ;};

The implementation of scoped_ptr is very similar to that of auto_ptr. The difference is that scoped_ptr has more strict restrictions on use-it cannot be copied, which means scoped_ptr cannot be converted to its ownership.

In general, if you do not need to copy or assign values to the pointer content, but only to prevent memory leakage, scoped_ptr is fully qualified.

 Shared_ptr share ownership, reference count

Shared_ptr is implemented by reference count. When copying or assigning values, the reference count is added to 1. In the analysis, space is released only when the reference count is reduced to 0, otherwise, you only need to reduce the reference count by 1.

(Ps: shared_ptr is called unique_ptr in the new C ++ 11 standard)

Template <typename T> class SharedPtr {public: SharedPtr (T * ptr = NULL): _ ptr (ptr), _ refCount (new long (1 )){}~ SharedPtr () {_ Release ();} SharedPtr (const SharedPtr <T> & sp): _ ptr (sp. _ ptr), _ refCount (sp. _ refCount) {++ (* _ refCount);} // traditional writing method/* SharedPtr <T> & operator = (const SharedPtr <T> & sp) {if (this! = & Sp) {this-> _ Release (); _ refCount = sp. _ refCount; _ ptr = sp. _ ptr; ++ (* _ refCount);} return * this;} * // modern SharedPtr <T> & operator = (SharedPtr <T> sp) {swap (_ ptr, sp. _ ptr); swap (_ refCount, sp. _ refCount); return * this;} T & operator * () {return * _ ptr;} T * operator-> () {return _ ptr;} T * GetPtr () {return _ ptr;} long GetCount () {return * _ refCount;} protected: void _ Release () {if (-- (* _ refCount) = 0) {// delete _ ptr; _ del (_ ptr); delete _ refCount ;}} protected: T * _ ptr; long * _ refCount; // reference count };

Why is the reference count set to the pointer type instead of an integer or static variable?

Please read this article: http://www.cnblogs.com/Lynn-Zhang/p/5400714.html

Sometimes seemingly perfect things often have hidden defects, and the above implementation still has problems!

The problem is as follows:

FirstIt is easy to understand. We can add a mutex lock when changing the reference count to preventMultithreadingRisks.

Second, circular references.Let's take a look at the Code implemented using shared_ptr of the Standard Library:

#include<iostream>using  namespace std;#include<memory>struct Node{shared_ptr<Node> _prev;shared_ptr<Node> _next;~Node(){cout << "delete :" <<this<< endl;}};void TestSharedPtr(){shared_ptr<Node> cur(new(Node));shared_ptr<Node> next(new(Node));}

  

Cur and next are out of scope, and both calls to the Destructor are released. It seems correct, but if we add two codes, the problem arises:

void TestSharedPtr(){shared_ptr<Node> cur(new(Node));shared_ptr<Node> next(new(Node));cur->_next = next;    // 1next->_prev = cur;    // 2}

  

We found that both nodes are not released! Let's analyze the cause: after the two sentences are added, the reference count of the two nodes increases by 1. Both objects cannot be released when the scope is out for analysis, because the premise of prev release is to release next, and the release of next depends on the release of prev. In the end, circular references are formed, and no one can release them.

Solution:

Template <class T> struct Node {public :~ Node () {cout <"delete:" <this <endl;} public: weak_ptr <Node> _ prev; weak_ptr <Node> _ next ;}; void TestWeakPtr () {shared_ptr <Node> cur (new Node (); shared_ptr <Node> next (new Node (); cout <"before connection: "<endl; cout <" cur: "<cur. use_count () <endl; cout <"next:" <next. use_count () <endl; cur-> _ next = next; next-> _ prev = cur; cout <"connected:" <endl; cout <"cur:" <cur. use_count () <endl; cout <"next:" <next. use_count () <endl ;}

 

Because weak_ptr (smart pointer for weak references) will perform special processing on the reference count (1 is not added in the above case ).

Third, customize the deleteer.Shared_ptr can only process and release the space opened by new, but cannot process malloc and fopen file pointers.Custom deleteerTo easily Delete pointers of other types, and its implementation is throughImitation Functions(By reloading operator.

Template <typename T, class Deleter = Del <T> class SharedPtr {public: SharedPtr (T * ptr); SharedPtr (T * ptr, Deleter del ); sharedPtr (const SharedPtr <T, Deleter> & ap );~ SharedPtr (); // SharedPtr <T, Deleter> & operator = (const SharedPtr <T, Deleter> & ptr); // traditional writing method // SharedPtr Modern Writing Method <T, deleter> & operator = (SharedPtr <T, Deleter> ap); T & operator * () const; T * operator-> () const; long GetCount () const; T * GetPtr () const; protected: void _ realted (); protected: T * _ ptr; long * _ pCount; Deleter _ del;}; template <typename T, class Deleter = Del <T> SharedPtr <T, Deleter>: SharedPtr (T * ptr): _ ptr (ptr), _ PCount (new long (1) {} template <typename T, class Deleter = Del <T> SharedPtr <T, Deleter>: SharedPtr (T * ptr, deleter del): _ ptr (ptr), _ pCount (new long (1), _ del (del) {} template <typename T, class Deleter = Del <T> SharedPtr <T, Deleter >:: SharedPtr (const SharedPtr <T, Deleter> & ap): _ ptr (ap. _ ptr), _ pCount (ap. _ pCount), _ del (ap. _ del) {++ (* this-> _ pCount);} template <typename T, class Deleter = Del <T> Sha RedPtr <T, Deleter> ::~ SharedPtr () {this-> _ reallocate () ;}// template <typename T, class Deleter = Del <T> // traditional writing method // SharedPtr <T, deleter> & SharedPtr <T, Deleter>: operator = (SharedPtr <T, Deleter> ap) // {// if (this-> _ ptr! = Ap. _ ptr) // {// this-> _ reallocate (); // this-> _ ptr = ap. _ ptr; // this-> _ pCount = ap. _ pCount; // ++ (* this-> _ pCount); //} // return * this; //} template <typename T, class Deleter = Del <T> // modern SharedPtr writing <T, Deleter> & SharedPtr <T, Deleter >:: operator = (SharedPtr <T, Deleter> ap) {swap (this-> _ ptr, ap. _ ptr); swap (this-> _ pCount, ap. _ pCount); swap (this-> _ del, ap. _ del); return * this;} template <typename T, class Deleter = Del <T> T & SharedPtr <T, Deleter >:: operator *() const {return * (this-> _ ptr);} template <typename T, class Deleter = Del <T> T * SharedPtr <T, Deleter> :: operator-> () const {return this-> _ ptr;} template <typename T, class Deleter = Del <T> long SharedPtr <T, Deleter >:: GetCount () const {return * (this-> _ pCount);} template <typename T, class Deleter = Del <T> T * SharedPtr <T, Deleter >:: GetPtr () const {return this-> _ ptr;} template <typename T, class Deleter = Del <T> void SharedPtr <T, Deleter >:: _ realpartition () {if (-- (* this-> _ pCount) = 0) {_ del (_ ptr); delete this-> _ pCount ;}} template <typename T> struct Free {void operator () (T * ptr) {cout <"Free:" <ptr <endl; free (ptr );}}; template <typename T> struct Fclose {void operator () (T * ptr) {cout <"Fclose:" <ptr <endl; fclose (ptr );}}; // test code void TestDelete () {int * p1 = (int *) malloc (sizeof (int); SharedPtr <int, Free <int> sp1 (p1 ); FILE * p2 = fopen ("test.txt", "r"); SharedPtr <FILE, Fclose <FILE> sp2 (p2 );}

  

Summary:

Transfer of auto_ptr management right-> not recommended

Scopd_ptr anti-copy-> simple and crude

Shared_ptr reference count-> increase or decrease the reference count until the reference count of the object is 0.

Scoped_array and shared_array manage object arrays. Because the implementation of scopd_ptr and scoped_array, shared_ptr and shared_array is similar, we will not repeat them here. Note that operator [] is reloaded for both of these mechanisms.

Week_ptr weak pointer-> secondary shared_ptr solves the problem of circular reference.

 

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.