Design and use of smart pointers in C + +

Source: Internet
Author: User

smart pointer (smart pointer) is a class that stores pointers to dynamically allocated (heap) objects and is used for lifetime control to ensure that dynamically allocated objects are destroyed automatically and correctly to prevent memory leaks. One of its common implementation techniques is the use of reference counts (reference count). The smart pointer class relates a counter to the object that the class points to, and the reference count 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, and when the object is created as a copy of another object, the copy constructor copies the pointer and increments the corresponding reference count; When an object is assigned, the assignment operator reduces the reference count of the object referred to by the left operand, if the reference count is reduced Delete the object) and increase 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 base object is deleted). A smart pointer is a class that simulates a pointer action. All smart pointers are overloaded with and * operators. Smart pointers also have many other features that are more useful for automatic destruction. This is mainly the use of the finite scope of the stack object and the temporary object (finite scope implementation) destructor to free memory. Of course, smart pointers are more than that, including the ability to modify source objects when copying. The smart pointer is different according to the requirements, the design is also different (copy on write, assignment is to release the object has permissions, reference count, etc.). Auto_ptr is a common smart pointer.
Smart pointers are typically implemented with class templates:
[CPP]View Plaincopy
  1. Template <class t>
  2. Class Smartpointer
  3. {
  4. Private
  5. T *_ptr;
  6. Public
  7. Smartpointer (T *p): _ptr (P) //constructor
  8. {
  9. }
  10. t& operator * () //overloaded * operator
  11. {
  12. return *_ptr;
  13. }
  14. t* operator---() //heavy-duty operator
  15. {
  16. return _ptr;
  17. }
  18. ~smartpointer () //destructor
  19. {
  20. Delete _ptr;
  21. }
  22. };
There are two classic strategies for implementing reference counting, one of which will be used here, where you need to define a separate concrete class to encapsulate the reference count and related pointers:
[CPP]View Plaincopy
  1. Defines the U_ptr class that is used only by the Hasptr class to encapsulate usage counts and related pointers
  2. 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
  3. Set the Hasptr class to friends so that its members can access the members of U_PTR
  4. Class U_ptr
  5. {
  6. friend class Hasptr;
  7. int *ip;
  8. size_t use;
  9. U_PTR (int *p): IP (P), use (1)
  10. {
  11. cout << "U_ptr constructor called!" << Endl;
  12. }
  13. ~u_ptr ()
  14. {
  15. Delete IP;
  16. cout << "u_ptr distructor called!" << Endl;
  17. }
  18. };
The Hasptr class needs a destructor to remove the pointer. However, the destructor cannot remove the pointer unconditionally. ”
The condition is the reference count. If the object is referred to by two pointers, then deleting one of the pointers does not invoke the destructor for the pointer, because there is another pointer pointing to the object at this point. It appears that the smart pointer is primarily to prevent improper destructor behavior and to prevent dangling pointers from appearing.

As shown, hasptr is the smart pointer, U_ptr is the counter, there is a variable use and pointer Ip,use records the *IP object is referred to by how many hasptr objects. Assuming now another two Hasptr object P1, P2 point to u_ptr, then I delete p1,use variable will be reduced by 1, u_ptr will not be destructor, then u_ptr point to the object is not destructor, then P2 still point to the original object, and not become a dangling pointer. When delete P2, the use variable will be reduced from 1 to 0. At this point, the U_ptr object is refactored, then the object pointed to by U_ptr is also refactored to ensure that no memory leaks occur.
Classes that contain pointers need to pay particular attention to replication control, because copying the pointer only copies the address in the pointer, not the object pointed to by the pointer.
Most C + + classes manage pointer members in one of three ways
(1) Regardless of the pointer member. Copy only the pointer, not the object pointed to by the pointer. 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) Copy the pointer when copying, and also copy the object pointed to by the pointer. This can result in wasted space. It is not necessarily necessary to copy the object to which the pointer is pointing.
(3) The Third kind is a kind of compromise way. A secondary class is used to manage the replication of pointers. There is a pointer to the auxiliary class in the original class, the data member of the auxiliary class is a counter and a pointer (pointing 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's decision to refuse is simple, if an object is not referenced, then the object is garbage. The system can be recycled.
The HASPTR smart pointer is declared as follows, a pointer to the U_ptr object is saved, and the U_ptr object points to the actual int base object, the code is as follows:
[CPP]View Plaincopy
  1. #include <iostream>
  2. Using namespace std;
  3. Defines the U_ptr class that is used only by the Hasptr class to encapsulate usage counts and related pointers
  4. 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
  5. Set the Hasptr class to friends so that its members can access the members of U_PTR
  6. Class U_ptr
  7. {
  8. friend class Hasptr;
  9. int *ip;
  10. size_t use;
  11. U_PTR (int *p): IP (P), use (1)
  12. {
  13. cout << "U_ptr constructor called!" << Endl;
  14. }
  15. ~u_ptr ()
  16. {
  17. Delete IP;
  18. cout << "u_ptr distructor called!" << Endl;
  19. }
  20. };
  21. Class Hasptr
  22. {
  23. Public
  24. //constructor: P is a pointer to an int object that has been created dynamically
  25. HASPTR (int *p, int i): ptr (new U_ptr (P)), Val (i)
  26. {
  27. cout << "Hasptr constructor called!" << "use =" << ptr->use << Endl;
  28. }
  29. //Copy constructor: Copy member and use Count plus 1
  30. Hasptr (const hasptr& orig): ptr (Orig.ptr), Val (orig.val)
  31. {
  32. ++ptr->use;
  33. cout << "Hasptr copy constructor called!" << "use =" << ptr->use << Endl;
  34. }
  35. //Assignment operator
  36. hasptr& operator= (const hasptr&);
  37. //destructor: Delete the U_ptr object if the count is 0
  38. ~hasptr ()
  39. {
  40. cout << "hasptr distructor called!" << "use =" << ptr->use << Endl;
  41. if (--ptr->use = = 0)
  42. delete ptr;
  43. }
  44. //Get data members
  45. int *get_ptr () const
  46. {
  47. return ptr->ip;
  48. }
  49. int Get_int () const
  50. {
  51. return Val;
  52. }
  53. //Modify data members
  54. void set_ptr (int *p) const
  55. {
  56. Ptr->ip = p;
  57. }
  58. void Set_int (int i)
  59. {
  60. val = i;
  61. }
  62. //Returns or modifies the base int object
  63. int Get_ptr_val () const
  64. {
  65. return *ptr->ip;
  66. }
  67. void Set_ptr_val (int i)
  68. {
  69. *ptr->ip = i;
  70. }
  71. Private
  72. U_ptr *ptr; //point to use the Count class U_ptr
  73. int val;
  74. };
  75. hasptr& Hasptr::operator = (const hasptr &RHS) //Note, here the assignment operator adds 1 to the use of RHS before reducing the use count of the operands, thus preventing self-assignment
  76. {
  77. //Increase usage count in right-hand operand
  78. ++rhs.ptr->use;
  79. //The use count of the left operand object is reduced by 1, and if the object's usage count is reduced to 0, the object is deleted
  80. if (--ptr->use = = 0)
  81. delete ptr;
  82. ptr = rhs.ptr; //Copy u_ptr pointer
  83. val = rhs.val; //Copy int member
  84. return * this;
  85. }
  86. int main (void)
  87. {
  88. int *pi = new int (42);
  89. Hasptr *HPA = new Hasptr (pi, 100); //Constructors
  90. Hasptr *HPB = new Hasptr (*HPA); //copy constructor
  91. Hasptr *HPC = new Hasptr (*HPB); //copy constructor
  92. Hasptr HPD = *HPA; //Copy constructor
  93. cout << hpa->get_ptr_val () << "<< hpb->get_ptr_val () << Endl;
  94. Hpc->set_ptr_val (10000);
  95. cout << hpa->get_ptr_val () << "<< hpb->get_ptr_val () << Endl;
  96. Hpd.set_ptr_val (10);
  97. cout << hpa->get_ptr_val () << "<< hpb->get_ptr_val () << Endl;
  98. Delete hpa;
  99. Delete HPB;
  100. Delete HPC;
  101. cout << hpd.get_ptr_val () << Endl;
  102. return 0;
  103. }
The assignment operator here is troublesome, and let me analyze it with a chart:
Suppose you now have two smart pointers, p1, p2, a point of memory with content 42, and a pointer to 100 of memory, such as:

Now, I'm going to do the assignment, p2 = p1. Compared with the above
[CPP]View Plaincopy
    1. hasptr& operator= (const hasptr&); //assignment operator
At this point, RHS is P1, first, the P1 point to the PTR's use plus 1,
[CPP]View Plaincopy
    1. ++rhs.ptr->use; //Increase usage count in right-hand operand
Then, do:
[CPP]View Plaincopy
    1. if (--ptr->use = = 0)
    2. delete ptr;
Because the original P2 object is now P2 not pointing, then the object is less a pointer to refer to, so, use do self minus 1;
At this point, the condition is established. Because the use of U2 is 1. Then, run the U_ptr destructor, and in the U_ptr destructor, do the delete IP operation, so freed the memory, there is no memory leak problem.
The next operation is natural, needless to say:
[CPP]View Plaincopy
    1. ptr = rhs.ptr; //Copy u_ptr pointer
    2. val = rhs.val; //Copy int member
    3. return * this;
After the assignment is done, it becomes as shown. The red label is the changed part:

It is also important to note that when overloading the assignment operator, it is important to note that the self-assignment is checked.


At this point, do p1 = p1 operation. So, first of all, the u1.use is increased by 1, which is 2, then the U1.use is reduced by 1 to 1. Then the delete operation is not performed and the rest of the operation can proceed smoothly. According to the C + + primer, "This assignment operator reduces the use count of RHS by 1 before reducing the use count of the left operand, thus preventing itself from being assigned". Hey, I understand that anyway. Of course, the assignment operator function can be as normal as it is:
[CPP]View Plaincopy
    1. if (this= = &RHS)
    2. return * this;
Running results such as:

Reprint please indicate source, original address: http://blog.csdn.net/hackbuteer1/article/details/7561235

Design and use of smart pointers in C + +

Related Article

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.