C++11 smart Pointers shared_ptr code details

  • 2020-09-16 07:44:02
  • OfStack

Smart Pointers in C++ first appear in the "quasi" standard library boost.

As more and more people are using it, C++11 has also introduced smart Pointers to manage dynamic objects in order to make it easier and safer for developers to use dynamic memory.

In the new standard, shared_ptr, unique_ptr, weak_ptr3 different types of smart Pointers are mainly provided.

In the next few articles, we'll summarize the use of these smart Pointers.

Today, let's first look at the shared_ptr smart pointer.

shared_ptr Smart pointer

shared_ptr is a 1 reference-counting smart pointer that is used to share object ownership meaning it allows multiple Pointers to the same object. This 1 is aligned with the original pointer 1.

Let's start with a simple piece of code to see the simple use of shared_ptr:


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

class Example
{
public:
 Example() : e(1) { cout << "Example Constructor..." << endl; }
 ~Example() { cout << "Example Destructor..." << endl; }

 int e;
};

int main() {

 shared_ptr<Example> pInt(new Example());
 cout << (*pInt).e << endl;
 cout << "pInt Reference counting : " << pInt.use_count() << endl;

 shared_ptr<Example> pInt2 = pInt;
 cout << "pInt Reference counting : " << pInt.use_count() << endl;
 cout << "pInt2 Reference counting : " << pInt2.use_count() << endl;
}

Program output is as follows:

[

Example Constructor...
pInt: 1
pInt reference count: 1
pInt reference count: 2
pInt2 reference count: 2
Example Destructor...

]

From the above code, we get some intuition about the shared_ptr pointer.

On the one hand, shared_ptr, like most container type 1 in STL, is a template class, so when you create shared_ptr, you need to specify the type it points to.

On the other hand, shared_ptr Pointers, like its name 1, allow multiple Pointers of that type to share an allocation object with 1 heap.

At the same time, shared_ptr USES the classic "reference count" method to manage object resources, and each shared_ptr object is associated with 1 Shared reference count.

The behavior of shared_ptr when copying and assigning is described in detail in C++Primer 5th Edition:

Each shared_ptr has an associated count value, commonly referred to as a reference count. Whenever we copy an shared_ptr, the counter increments.

For example, when 1 shared_ptr initializes another shred_ptr, or when it is passed as a parameter to a function and as the return value of the function, the counter it is associated with increments.

The counter decrements when we assign a new value to shared_ptr or when shared_ptr is destroyed (for example, a local shared_ptr leaves its scope).

Once 1 counter of shared_ptr becomes 0, it automatically releases its managed objects.

Comparing our code above, we can see that when we hand over a pointer to an Example object to pInt management, its associated reference count is 1.

Next, we initialize pInt2 with pInt, and the reference count associated with the two is increased to 2. At the end of the function, pInt and PInt2 leave the function to operate on, and the corresponding reference count decreases by 1 and finally becomes 0, so the Example object is automatically released (its destructor is called).

Next, let's give a complete overview of 1 common USES of shared_ptr:

1. Create an instance of shared_ptr

The safest and most efficient way is to call the make_shared library function, which allocates and initializes an object in the heap, and finally returns an instance of share_ptr pointing to this object.

If you don't want to use make_ptr, you can also specify an object for new and pass its original pointer to the constructor for share_ptr.

Examples are as follows:


int main() {

 //  Passed to the make_shared The parameters of the function must be and shared_ptr Matches a constructor of the type pointed to 
 shared_ptr<string> pStr = make_shared<string>(10, 'a');
 cout << *pStr << endl; // aaaaaaaaaa

 int *p = new int(5);
 shared_ptr<int> pInt(p);
 cout << *pInt << endl; // 5
}

2. Access the object referred to

shared_ptr is used in a similar way to normal Pointers, using either the de-reference operator * to get the original object and access its individual members, or the pointer accessor - > To access the individual members of the original object.

3. Copy and assignment

We can use one shared_ptr object to initialize another instance of share_ptr, which increases its reference count.

Such as:


int main() {
 shared_ptr<string> pStr = make_shared<string>(10, 'a');
 cout << pStr.use_count() << endl; // 1

 shared_ptr<string> pStr2(pStr);
 cout << pStr.use_count() << endl; // 2
 cout << pStr2.use_count() << endl; // 2
}

If shared_ptr instance p and another instance shared_ptr instance q point to the same type or can be converted to each other, we can also perform assignment operations such as p = q.

This operation decrements the p reference count and increments the q reference count.

Such as:


class Example
{
public:
 Example(string n) : name(n) { cout << n << " constructor..." << endl; }
 ~Example() { cout << name << " destructor..." << endl; }

 string name;
};

int main() {

 shared_ptr<Example> pStr = make_shared<Example>("a object");
 shared_ptr<Example> pStr2 = make_shared<Example>("b object");
 cout << pStr.use_count() << endl;
 cout << pStr2.use_count() << endl;

 pStr = pStr2; //  Since then pStr and pStr Point to the same object 
 cout << pStr->name << endl;
 cout << pStr2->name << endl;
}

The output is as follows:

[

a object constructor...
b object constructor...
1
1
a object destructor...
b object
b object
b object destructor...

]

4. Check the reference count

shared_ptr provides two functions to check the reference count value it shares, unique() and use_count().

Previously, we have used the use_count() function many times, which returns the reference count value of the current pointer. It is worth noting that the use_count() function may be inefficient and should only be used for testing or debugging.

The unique() function is used to test whether the shared_ptr is the only owner of the original pointer, that is, use_count() returns true if its return value is 1, or false if it is not.

Example:


int main() {
 shared_ptr<string> pStr = make_shared<string>(10, 'a');
 cout << pStr.unique() << endl; // true

 shared_ptr<string> pStr2(pStr);
 cout << pStr2.unique() << endl; // false;
}

conclusion


Related articles: