C++11 smart Pointers weak_ptr detail

  • 2020-09-16 07:43:39
  • OfStack

For example, today we are going to talk about C++11 introduced in three smart Pointers: weak_ptr.

It is best to know something about shared_ptr before studying weak_ptr. If you don't already know what shared_ptr is, take a look at another article:

[C++11 new feature] C++11 smart pointer shared_ptr

1. Why is weak_ptr needed?

Before we introduce weak_ptr formally, let's recall some facts about shared_ptr.

We know that shared_ptr is a smart pointer that USES reference counting. Multiple instances of shared_ptr can point to the same dynamic object and maintain a Shared reference counter.

The problem of circular references (or circular references) is always unavoidable with reference counting, and shared_ptr is no exception.

Let's start with this example:


#include <iostream>
#include <memory>
#include <vector>
using namespace std;

class ClassB;

class ClassA
{
public:
 ClassA() { cout << "ClassA Constructor..." << endl; }
 ~ClassA() { cout << "ClassA Destructor..." << endl; }
 shared_ptr<ClassB> pb; //  in A Referenced in the B
};

class ClassB
{
public:
 ClassB() { cout << "ClassB Constructor..." << endl; }
 ~ClassB() { cout << "ClassB Destructor..." << endl; }
 shared_ptr<ClassA> pa; //  in B Referenced in the A
};

int main() {
 shared_ptr<ClassA> spa = make_shared<ClassA>();
 shared_ptr<ClassB> spb = make_shared<ClassB>();
 spa->pb = spb;
 spb->pa = spa;
 //  Function over, think about it 1 Bottom: spa and spb Will resources be released? 
}

The output of the above code is as follows:


ClassA Constructor...
ClassB Constructor...
Program ended with exit code: 0

From the above code, there is a circular reference between ClassA and ClassB. We can see from the running results that when the main function is finished, the dynamic resources managed by spa and spb are not released, resulting in a memory leak.

To solve problems like this, C++11 introduced weak_ptr to break this circular reference.

What is weak_ptr?

weak_ptr is a smart pointer introduced to match shared_ptr. It points to an object managed by shared_ptr without affecting the life cycle of the object referred to. In other words, binding 1 weak_ptr to 1 shared_ptr does not change the reference count of shared_ptr.

Once the last shared_ptr pointing to an object is destroyed, the object is released, whether or not there is an weak_ptr pointing to the object.

From this perspective, weak_ptr is more like a helper than a smart pointer to shared_ptr.

3. How to use weak_ptr?

Next, let's look at the simple use of weak_ptr.

3.1 How do I create an instance of weak_ptr

When we create 1 weak_ptr, we need 1 instance of shared_ptr to initialize weak_ptr. As it is a weak share, the creation of weak_ptr does not affect the reference count of shared_ptr.

Example:


int main() {
 shared_ptr<int> sp(new int(5));
 cout << " Before creating sp Reference count of: " << sp.use_count() << endl; // use_count = 1

 weak_ptr<int> wp(sp);
 cout << " After creation sp Reference count of: " << sp.use_count() << endl; // use_count = 1
}

3.2 How to determine whether weak_ptr points to an object

Since weak_ptr does not change the reference count of instances of shared_ptr that it shares, it is possible that objects pointed to by weak_ptr are freed.

At this point, we cannot access the object directly using weak_ptr. So how do we tell if weak_ptr points to an object?

The lock function is provided in C++ to achieve this function.

The lock() function returns 1 shared_ptr pointing to the Shared object if the object exists, or 1 empty shared_ptr.

Example:


class A
{
public:
 A() : a(3) { cout << "A Constructor..." << endl; }
 ~A() { cout << "A Destructor..." << endl; }

 int a;
};

int main() {
 shared_ptr<A> sp(new A());
 weak_ptr<A> wp(sp);
 //sp.reset();

 if (shared_ptr<A> pa = wp.lock())
 {
 cout << pa->a << endl;
 }
 else
 {
 cout << "wp The point object is empty " << endl;
 }
}

Try to comment out the sp.reset () line and see what the difference is.

In addition, weak_ptr provides the expired() function to determine whether the object has been destroyed.

Example:


class A
{
public:
 A() : a(3) { cout << "A Constructor..." << endl; }
 ~A() { cout << "A Destructor..." << endl; }

 int a;
};

int main() {
 shared_ptr<A> sp(new A());
 weak_ptr<A> wp(sp);
 sp.reset(); //  At this time sp Be destroyed 
 cout << wp.expired() << endl; // true It means it has been destroyed, otherwise it is false
}

The code input is as follows:

[

A Constructor...
A Destructor...
1

]

3.3 How to use weak_ptr

weak_ptr does not overload operator- > And the operator * operators, so you can't use an object directly through weak_ptr. The typical use is to call its lock function to get the shared_ptr example, which then accesses the original object.

Finally, let's take a look at how to use weak_ptr to modify the original code and break the circular reference problem.


class ClassB;

class ClassA
{
public:
 ClassA() { cout << "ClassA Constructor..." << endl; }
 ~ClassA() { cout << "ClassA Destructor..." << endl; }
 weak_ptr<ClassB> pb; //  in A Referenced in the B
};

class ClassB
{
public:
 ClassB() { cout << "ClassB Constructor..." << endl; }
 ~ClassB() { cout << "ClassB Destructor..." << endl; }
 weak_ptr<ClassA> pa; //  in B Referenced in the A
};

int main() {
 shared_ptr<ClassA> spa = make_shared<ClassA>();
 shared_ptr<ClassB> spb = make_shared<ClassB>();
 spa->pb = spb;
 spb->pa = spa;
 //  Function over, think about it 1 Bottom: spa and spb Will resources be released? 
}

The output results are as follows:

[

ClassA Constructor...
ClassB Constructor...
ClassA Destructor...
ClassB Destructor...
Program ended with exit code: 0

]

You can see from the results that both spa and spb point to objects that have been freed!

conclusion


Related articles: