How to design and use smart pointers in C ++

Source: Internet
Author: User

Smart pointer is a class that stores pointer to a dynamic allocation (HEAP) object. It is used for lifetime control and ensures automatic and correct destruction of dynamically allocated objects to prevent memory leakage. One of its general implementations is the use of reference count ). The smart pointer class associates a counter with the object to which the class points. The reference counting class tracks how many objects in the class share the same pointer. Each time a new object of the class is created, the pointer is initialized and the reference count is set to 1. when an 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 constructor reduces the reference count (if the reference count is reduced to 0, the base object is deleted ).
A smart pointer is a class that simulates pointer actions. All Smart pointers are reloaded-> and * operators. Smart pointers also have many other functions, which are useful for automatic destruction. This mainly uses the limited scope of the stack object and the temporary object (limited scope implementation) destructor to release the memory. Of course, smart pointers also include modifying source objects during replication. Smart pointers are designed differently based on different requirements (copy upon writing, assign values to release objects with permissions, reference counts, control transfer, etc ). Auto_ptr is a common smart pointer.
Smart pointers are usually implemented using class templates: Copy codeThe Code is as follows: template <class T>
Class smartpointer
{
Private:
T * _ ptr;
Public:
Smartpointer (T * p): _ ptr (p) // Constructor
{
}
T & operator * () // overload * operator
{
Return * _ ptr;
}
T * operator-> () // overload-> operator
{
Return _ ptr;
}
~ Smartpointer () // destructor
{
Delete _ ptr;
}
};

There are two classic policies for implementing reference counting, one of which will be used here. In the method used here, you need to define a separate specific class to encapsulate reference counting and related pointers:Copy codeThe Code is as follows: // defines the U_Ptr class used only by the HasPtr class, which is used to encapsulate the use of counting and related pointers.
// All the members of this class are private. We do not want normal users to use the U_Ptr class, so it does not have any public members.
// Set the HasPtr class as a friend so that its members can access U_Ptr members.
Class U_Ptr
{
Friend class HasPtr;
Int * ip address;
Size_t use;
U_Ptr (int * p): ip (p), use (1)
{
Cout <"U_ptr constructor called! "<Endl;
}
~ U_Ptr ()
{
Delete ip;
Cout <"U_ptr distructor called! "<Endl;
}
};

The HasPtr class requires an destructor to delete pointers. However, destructor cannot delete pointers unconditionally ."
The condition is the reference count. If the object is referred to by two pointers, deleting one of the pointers does not call the destructor of the pointer, because another pointer points to the object. It seems that smart pointers mainly prevent improper destructor and suspension pointers.

As shown in, HasPtr is a smart pointer, and U_Ptr is a counter. There is a variable use and pointer ip, and use records the number of HasPtr objects that indicate the ip object. Assume that another two HasPtr objects p1 and p2 point to U_Ptr. Now, when I delete p1, the use variable will be automatically reduced by 1, and the U_Ptr will not analyze the structure, so the U_Ptr points to the object will not be destructed, so p2 still points to the original object, and will not become a floating pointer. When delete p2 is used, the use variable is reduced from 1 to 0. At this time, the U_Ptr object is destructed, and the object pointed to by U_Ptr is also destructed to ensure that there is no memory leakage.
For classes that contain pointers, pay special attention to replication control because only the addresses in the pointers are copied instead of the objects pointed to by the pointers.
Most C ++ classes use one of the three methods to manage pointer members.
(1) No matter the pointer member. Only the pointer is copied. When a pointer releases the space of the object to which it points, other pointers become floating pointers. This is an extreme
(2) When copying, that is, copying the pointer and copying the object pointed to by the pointer. This may cause a waste of space. Because the copying of objects pointed to by pointers is not necessarily necessary.
(3) The third is a method of compromise. A helper class is used to manage pointer replication. The original class has a pointer pointing to the helper class. The data member of the helper class is a counter and a pointer (pointing to the original) (this is the smart pointer implementation method ).
In fact, the reference count of smart pointers is similar to java's garbage collection mechanism: java's garbage determination is very simple. If an object is not referenced, this object is garbage. The system can be recycled.
The HasPtr smart pointer statement is as follows. Save a pointer to the U_Ptr object. The U_Ptr object points to the actual basic int object. The Code is as follows:Copy codeThe Code is as follows: # include <iostream>
Using namespace std;

// Defines the U_Ptr class used only by the HasPtr class, which is used to encapsulate the use of counting and related pointers.
// All the members of this class are private. We do not want normal users to use the U_Ptr class, so it does not have any public members.
// Set the HasPtr class as a friend so that its members can access U_Ptr members.
Class U_Ptr
{
Friend class HasPtr;
Int * ip address;
Size_t use;
U_Ptr (int * p): ip (p), use (1)
{
Cout <"U_ptr constructor called! "<Endl;
}
~ U_Ptr ()
{
Delete ip;
Cout <"U_ptr distructor called! "<Endl;
}
};

Class HasPtr
{
Public:
// Constructor: p is the pointer to the dynamically created int object.
HasPtr (int * p, int I): ptr (new U_Ptr (p), val (I)
{
Cout <"HasPtr constructor called! "<" Use = "<ptr-> use <endl;
}

// Copy constructor: copy the Member and Add 1 to the count
HasPtr (const HasPtr & orig): ptr (orig. ptr), val (orig. val)
{
++ Ptr-> use;
Cout <"HasPtr copy constructor called! "<" Use = "<ptr-> use <endl;
}

// Value assignment operator
HasPtr & operator = (const HasPtr &);

// Destructor: If the count is 0, the U_Ptr object is deleted.
~ HasPtr ()
{
Cout <"HasPtr distructor called! "<" Use = "<ptr-> use <endl;
If (-- ptr-> use = 0)
Delete ptr;
}

// Obtain data members
Int * get_ptr () const
{
Return ptr-> ip address;
}
Int get_int () const
{
Return val;
}

// Modify the data member
Void set_ptr (int * p) const
{
Ptr-> ip = p;
}
Void set_int (int I)
{
Val = I;
}

// Return or modify the base int object
Int get_ptr_val () const
{
Return * ptr-> ip address;
}
Void set_ptr_val (int I)
{
* Ptr-> ip = I;
}
Private:
U_Ptr * ptr; // indicates the U_Ptr class used for counting.
Int val;
};
HasPtr & HasPtr: operator = (const HasPtr & rhs) // note that the value assignment operator adds 1 to the rhs Usage Technology before reducing the number of operations used. This prevents self-assignment.
{
// Add the count in the right operand
++ Rhs. ptr-> use;
// Subtract 1 from the count of the left operand object. If the count of the object is reduced to 0, the object will be deleted.
If (-- ptr-> use = 0)
Delete ptr;
Ptr = rhs. ptr; // copy the U_Ptr pointer
Val = rhs. val; // copy the int Member
Return * this;
}

Int main (void)
{
Int * pi = new int (42 );
HasPtr * hpa = new HasPtr (pi, 100); // Constructor
HasPtr * hpb = new HasPtr (* hpa); // copy the constructor
HasPtr * hpc = new HasPtr (* hpb); // copy the constructor
HasPtr hpd = * hpa; // copy the constructor

Cout Hpc-> set_ptr_val (10000 );
Cout Hpd. set_ptr_val (10 );
Cout Delete hpa;
Delete hpb;
Delete hpc;
Cout Return 0;
}

The value assignment operator here is troublesome, and I want to analyze it using charts:
Assume that there are two smart pointers p1 and p2, one pointing to the memory with the content 42, and the other pointing to the memory with the content 100, for example:

Now, I want to assign values, p2 = p1. Compared with the above

Copy codeThe Code is as follows:
HasPtr & operator = (const HasPtr &); // value assignment operator

In this case, rhs is p1. First, add 1 to the use of the ptr to which p1 points,

Copy codeThe Code is as follows:
++ Rhs. ptr-> use; // increase the use count in the right operand.

Then, do:

Copy codeThe Code is as follows:
If (-- ptr-> use = 0)
Delete ptr;

Because the object that p2 points to is not pointing to at present, the object will be missing a pointer, so use will perform auto-minus 1;
The condition is true. Because u2's use is 1. Then, run the U_Ptr destructor, and in the U_Ptr destructor, perform the delete ip operation. Therefore, the memory is released without memory leakage.
The following operations are natural and do not need to be repeated:

Copy codeThe Code is as follows:
Ptr = rhs. ptr; // copy the U_Ptr pointer
Val = rhs. val; // copy the int Member
Return * this;

After the assignment is completed, it becomes as shown in. The red mark is the changed part:

Note that when you overload the value assignment operator, you must check your self-assignment status.
:

In this case, perform the p1 = p1 operation. First, the u1.use auto increment value is 1, which is 2. Then, the u1.use auto increment value is 1. Then the delete operation will not be executed, and the remaining operations will be smooth. According to C ++ primer, "This value assignment operator adds 1 to the rhs usage count before reducing the Count used by the left operand, thus preventing the assignment by itself ". Well, I understand it that way. Of course, one of the assignment operator functions can follow the conventional method:

Copy codeThe Code is as follows:
If (this = & rhs)
Return * this;

The running result is as follows:

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.