C + + weak reference smart pointer weak_ Use of PTR

Posted by AmbroseChapel on Wed, 19 Jan 2022 08:18:58 +0100

weak_ptr is also a reference counting smart pointer, but it does not increase the reference count of objects, that is, weak references. In contrast, shared_ptr is a strong reference, as long as there is a shared to the object_ If PTR exists, the object will not be destructed until it points to the last shared of the object_ PTR will not be destroyed until it is destructed or reset().

Use weak_ptr, we can solve the common null pointer problem and circular reference problem.

1, Null pointer problem

What is a dangling pointer? Consider the following:

There are two pointers p1 and p2 to the same Object on the heap. p1 and p2 are located in different threads. Suppose thread A destroys the Object through the p1 pointer (although p1 is set to NULL), p2 becomes A NULL pointer. This is A typical C/C + + memory error.

Use weak_ptr can help us easily solve the above null pointer problem.

weak_ptr does not control the lifetime of an object, but it knows whether the object is still alive. If the object is still alive, it can be promoted to a valid shared_ptr (the lifting operation obtains the strong reference pointer of the managed object through the lock() function); If the object is dead, the promotion will fail and an empty shared will be returned_ ptr. The example code is as follows:

#include <iostream>
#include <memory>

int main()
{
    // OLD, problem with dangling pointer
    // PROBLEM: ref will point to undefined data!

    int* ptr = new int(10);
    int* ref = ptr;
    delete ptr;

    // NEW
    // SOLUTION: check expired() or lock() to determine if pointer is valid

    // empty definition
    std::shared_ptr<int> sptr;

    // takes ownership of pointer
    sptr.reset(new int);
    *sptr = 10;

    // get pointer to data without taking ownership
    std::weak_ptr<int> weak1 = sptr;

    // deletes managed object, acquires new pointer
    sptr.reset(new int);
    *sptr = 5;

    // get pointer to new data without taking ownership
    std::weak_ptr<int> weak2 = sptr;

    // weak1 is expired!
    if(auto tmp = weak1.lock())
        std::cout << *tmp << '\n';
    else
        std::cout << "weak1 is expired\n";

    // weak2 points to new data (5)
    if(auto tmp = weak2.lock())
        std::cout << *tmp << '\n';
    else
        std::cout << "weak2 is expired\n";
}

2, Circular reference problem

A circular reference is as follows:

#include <iostream>
#include <memory>

using namespace std;

class Parent;
class Child; 

typedef shared_ptr<Parent> parent_ptr;
typedef shared_ptr<Child> child_ptr; 

class Parent
{
public:
       ~Parent() { 
              cout << "~Parent()" << endl; 
       }
public:
       child_ptr children;
};

class Child
{
public:
       ~Child() { 
              cout << "~Child()" << endl; 
       }
public:
       parent_ptr parent;
};

int main()
{
  parent_ptr father(new Parent);
  child_ptr son(new Child);

  // Father and son reference each other
  father->children = son;
  son->parent = father;

  cout << father.use_count() << endl;  // The reference count is 2
  cout << son.use_count() << endl;     // The reference count is 2

  return 0;
}

In the above code, before the program exits, the reference count of father is 2 and the count of son is 2. When the program exits, shared_ptr simply reduces the count by 1. If it is 0, it will be released. Obviously, in this case, the reference count is not 0, so the memory pointed by father and son cannot be released, resulting in memory leakage.

Use weak_ptr can break such circular references. Because the weak reference does not change the reference count, similar to the ordinary pointer, the circular reference can be released as long as one party of the circular reference uses the weak reference.

Take the above code as an example, just modify the code of Child class as follows:

class Child
{
public:
       ~Child() { 
              cout << "~Child()" << endl; 
       }
public:
       weak_ptr<Parent> parent; 
};

Finally, it is worth mentioning that although the circular reference can be effectively removed through the weak reference pointer, this method can only be used when the circular reference can be predicted, that is, this is only a compilation time solution. If the circular reference occurs during the running process of the program, it will still cause memory leakage. Therefore, do not think that memory leakage can be eliminated as long as smart pointers are used.