Smart pointers (smart pointer) are classes that store pointers to dynamically allocated (heap) objects for lifetime control, ensuring that dynamically allocated objects are automatically and correctly destroyed, preventing memory leaks. A common implementation of this technique is to use reference counting (reference count). A smart pointer class relates a counter to an object that the class points to, and a reference count tracks how many objects in the class share the same pointer. Initializes the pointer each time a new object of the class is created and a reference count of 1; When an object is created as a copy of another object, the copy constructor copies the pointer and increases the corresponding reference count; When an object is assigned, the assignment operator decreases the reference count of the object referred to by the left operand (if the reference count is reduced to 0, Deletes the object) and increases the reference count of the object referred to by the right operand; when the destructor is called, the constructor reduces the reference count (if the reference count is reduced to 0, the underlying object is deleted).
A smart pointer is a class that simulates the action of a pointer. All smart pointers will overload the-> and * operators. Smart pointers also have many other features that are more useful for automatic destruction. This mainly utilizes the finite scope of the stack object and the temporary object (finite scope implementation) destructor to free the memory. Of course, smart pointers are more than that, including modifying source objects as they are replicated. Smart pointers vary according to requirements, design is also different (write-time replication, assignment is to release the object has permissions, reference count, etc., control power transfer, etc.). Auto_ptr is a common smart pointer.
Smart pointers are typically implemented with class templates:
Copy Code code 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 strategies for implementing reference counting, where one is used, and the method used here needs to define a separate concrete class to encapsulate reference counts and related pointers:
Copy Code code as follows:
Define the U_ptr class that is used only by the Hasptr class to encapsulate usage counts and related pointers
All members of this class are private, and we don't want ordinary users to use the U_ptr class, so it doesn't have any public members
Set the Hasptr class as a friend so that its members can access members of the U_PTR
Class U_ptr
{
Friend class Hasptr;
int *ip;
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 a destructor to remove the pointer. However, destructors cannot unconditionally delete pointers. ”
The condition is the reference count. If the object is indicated by a two pointer, deleting one of the pointers does not call the destructor of the pointer because there is another pointer to the object at this point. It seems that the intelligent pointer is mainly to prevent improper deconstruction behavior, to prevent the emergence of dangling pointers.
As shown in the figure above, Hasptr is a smart pointer, u_ptr is a counter; there is a variable use and a pointer ip,use a record of how many Hasptr objects the *ip object refers to. Assuming that now two Hasptr objects P1, P2 point to U_ptr, now I delete the p1,use variable will be reduced by 1, u_ptr will not be destructor, then u_ptr point to the object will not be destructor, then P2 still point to the original object, and not become a dangling pointer. When delete P2, the use variable is reduced from 1 to 0. At this point, the U_ptr object is destructor, so the object that U_ptr points to is also destructor, ensuring that no memory leaks occur.
Classes that contain pointers need to pay special attention to replication control because copying the pointer only addresses the pointer, not the object that the pointer points to.
Most C + + classes manage pointer members in one of three ways
(1) Regardless of the pointer member. Copy only the pointer when copying, and do not copy the object that the pointer points to. When one of the pointers releases the space of the object it points to, the other pointers become floating pointers. This is an extreme
(2) When copying, that is, copy the pointer, also copy the object pointed to by the pointer. This may cause a waste of space. Because the copy of the object pointed to by the pointer is not necessarily necessary.
(3) The third type is a compromise. A secondary class is used to manage the replication of pointers. The original class has a pointer to the auxiliary class, and the data member of the auxiliary class is a counter and a pointer (to the original) (this is how the smart pointer is implemented).
In fact, the reference count of smart pointers is similar to the Java garbage collection mechanism: Java garbage is very simple to answer, if an object does not refer to the reference, then the object is garbage. The system can be recycled.
The HASPTR smart pointer is declared as follows, saving a pointer to the U_ptr object, u_ptr the object to the actual int base object, and the code is as follows:
Copy Code code as follows:
#include <iostream>
using namespace Std;
Define the U_ptr class that is used only by the Hasptr class to encapsulate usage counts and related pointers
All members of this class are private, and we don't want ordinary users to use the U_ptr class, so it doesn't have any public members
Set the Hasptr class as a friend so that its members can access members of the U_PTR
Class U_ptr
{
Friend class Hasptr;
int *ip;
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 a pointer to an int object that has been created dynamically
hasptr (int *p, int i): ptr (New U_ptr (P)), Val (i)
{
cout << "Hasptr constructor called!" << "use =" << ptr->use << Endl;
}
Copy constructor: Copy members and use Count plus 1
Hasptr (const hasptr& orig): ptr (Orig.ptr), Val (orig.val)
{
++ptr->use;
cout << "Hasptr copy constructor called!" << "use =" << ptr->use << Endl;
}
Assignment operator
hasptr& operator= (const hasptr&);
destructor: If the count is 0, delete the U_ptr object
~hasptr ()
{
cout << "Hasptr distructor called!" << "use =" << ptr->use << Endl;
if (--ptr->use = 0)
Delete ptr;
}
Get data members
int *get_ptr () const
{
Return ptr->ip;
}
int Get_int () const
{
return Val;
}
Modifying data members
void set_ptr (int *p) const
{
Ptr->ip = p;
}
void Set_int (int i)
{
val = i;
}
Returns or modifies the underlying int object
int Get_ptr_val () const
{
Return *ptr->ip;
}
void Set_ptr_val (int i)
{
*ptr->ip = i;
}
Private
U_ptr *ptr; Point to Use Count class U_ptr
int Val;
};
hasptr& Hasptr::operator = (const hasptr &RHS)//Note that here the assignment operator prevents self assignment by adding 1 to the usage of the RHS before reducing the use count of the operand.
{
Increase the usage count in the right-hand operand
++rhs.ptr->use;
Subtract the use count of the left operand object by 1, and delete the object if its usage count is reduced to 0
if (--ptr->use = 0)
Delete ptr;
ptr = rhs.ptr; Copy u_ptr pointer
val = rhs.val; Copy int Member
return *this;
}
int main (void)
{
int *pi = new int (42);
Hasptr *hpa = new Hasptr (pi, 100); Constructors
Hasptr *HPB = new Hasptr (*HPA); Copy Constructors
Hasptr *hpc = new Hasptr (*HPB); Copy Constructors
Hasptr HPD = *HPA; Copy Constructors
cout << hpa->get_ptr_val () << "" << hpb->get_ptr_val () << Endl;
Hpc->set_ptr_val (10000);
cout << hpa->get_ptr_val () << "" << hpb->get_ptr_val () << Endl;
Hpd.set_ptr_val (10);
cout << hpa->get_ptr_val () << "" << hpb->get_ptr_val () << Endl;
Delete hpa;
Delete HPB;
Delete HPC;
cout << hpd.get_ptr_val () << Endl;
return 0;
}
The assignment operator here is a bit cumbersome, and let me use a chart to analyze it:
Suppose there are now two smart pointers P1, p2, a memory pointing to content 42, a memory pointing to 100, as shown in the following figure:
Now, I'm going to do assignment, p2 = p1. Compared to the above
Copy Code code as follows:
hasptr& operator= (const hasptr&); Assignment operator
At this point, the RHS is the P1, first the P1 point to the PTR of use plus 1,
Copy Code code as follows:
++rhs.ptr->use; Increase the usage count in the right-hand operand
Then, do:
Copy Code code as follows:
if (--ptr->use = 0)
Delete ptr;
Because, the original P2 point object now P2 not point, then the object is less a pointer to refer to, so, use do since minus 1;
At this time, the condition is established. Because the use of U2 is 1. Then, the destructor of the u_ptr is run, and the delete IP operation is done in the U_ptr destructor, so the memory is freed and there is no memory leak.
The next operation is natural, needless to say:
Copy Code code as follows:
ptr = rhs.ptr; Copy u_ptr pointer
val = rhs.val; Copy int Member
return *this;
After the assignment is done, it is shown in the following figure. The red label is the part of the change:
It is also important to note that when overloading assignment operators, it is important to check that the self assignment.
As shown in the figure:
At this point, do p1 = p1 operation. So, first U1.use 1, 2, then u1.use from 1 to 1. Then the delete operation is not performed, and the rest of the operation can proceed smoothly. According to "C + + Primer", "This assignment operator reduces the use count of the left operand by 1, thereby preventing its own assignment". Oh, I understand that anyway. Of course, the assignment operator function can be as usual:
Copy Code code as follows:
if (this = = &RHS)
return *this;
The results of the operation are shown below: