Smart pointer
This section provides a rough understanding of smart pointers in the C + + standard library.
Why is there a smart pointer?
The existence of smart pointers has two advantages, all for one reason - it can automatically release the memory it points to.
First of all, programmers do not have to manage every pointer defined by it. The work of memory release is left to the smart pointer, which makes programmers lazy a lot.
Second, smart pointer is a kind of resource management class. They can control their own memory to make the program work normally.
Types of smart pointers
Any c + + book should mention smart pointers, so you should have a preliminary understanding of them, at least you should know how they are used.
There are four kinds of smart pointers
- auto_ptr (not used by c++11)
- unique_ptr
- shared_ptr
- weak_ptr
shared_ptr
shared_ptr inherited from_ Ptr_base, and_ Ptr_base contains two pointers:
element_type* _Ptr{nullptr}; _Ref_count_base* _Rep{nullptr};
_ Ptr is shared_ptr internal pointer to the allocated memory, and_ Rep refers to a base class used to query the number of references.
We all know shared_ptr internally maintains an integer that records the current number of smart pointers to the memory. The memory will be deleted only when the number is 0.
You can use:
shared_ptr<type>.use_count(); //Inherit from base class
To view the number of references to the current memory.
And inside it is return_ Rep->_ Use_ count()
_NODISCARD long use_count() const noexcept { return _Rep ? _Rep->_Use_count() : 0; }
So it's necessary to look at this_ Rep_count_base is something.
_ Rep_ count_ Two unsigned long are maintained inside the base:
_Atomic_counter_t _Uses = 1; //Current references _Atomic_counter_t _Weaks = 1; //weak_ptr count
So when_ What about the increase of Uses? How to increase?
Let's go through the process with a copy construction operation.
shared_ptr<int> _new(_old);
The copy constructor is:
template <class _Ty2, enable_if_t<_SP_pointer_compatible<_Ty2, _Ty>::value, int> = 0> shared_ptr(const shared_ptr<_Ty2>& _Other) noexcept { // construct shared_ptr object that owns same resource as _Other this->_Copy_construct_from(_Other); }
Called base class_ Copy_construct_from()
template <class _Ty2> void _Copy_construct_from(const shared_ptr<_Ty2>& _Other) noexcept { // implement shared_ptr's (converting) copy ctor //First, let the copied shared_ptr_ Uses increase _Other._Incref(); //Then add the_ Rep replication in the past, here is only pointer replication, they should point to the same_ Rep_count_base object _Ptr = _Other._Ptr; _Rep = _Other._Rep; }
OK, we're being tough.
Suppose we have two threads (A thread and B thread) that want to execute this copy construction behavior.
Now thread A enters_ Copy_constuct_from, just as thread A is about to_ When uses is added, the CPU decides to run thread B suddenly. As A result, after thread B runs to its copy function, it will make_ Uses plus 1. Then the CPU returns to thread A and takes out the data just saved_ The old value of uses then + 1.
As a result, there are two shared_ptr was successfully constructed, but_ Uses has only increased by one.
shared_ptr avoids the above problems with an atomic addition operation.
The underlying call_ The function of InterlockedIncrement can be understood as non interruptible, just like the V operation of semaphore.
Of course_ Use reduction and_ The same is true for leaks.
Next, I want to talk about shared_ptr's operator = (), its implementation is really interesting and ingenious.
Let's think about what we need to do if we assign values normally?
Suppose we execute a=b
First, let's make the area a points to_ Uses minus 1. If only a points to that memory, the program can reclaim the inaccessible memory according to the idea of resource management. Then let a point to the memory of b and b point to the memory of b_ Uses plus 1.
Think about it. It's troublesome.
And shared_ptr uses the swap operation to complete the assignment:
shared_ptr& operator=(const shared_ptr& _Right) noexcept { //Use_ Right assigns a value to construct a temporary anonymous shared_ptr, and then let * this exchange with this anonymous shared_ptr(_Right).swap(*this); return *this; }
Think about it, where are we_ Right has a newly constructed shared_ptr, making his_ Uses+1, and then exchange with * this without changing any_ Uses, and finally destruct the temporarily created anonymous shared_ptr, if_ If uses is 0, this memory will be released automatically.
By temporarily constructing an object and then exchanging it, it is a little clever to include all the above things.
weak_ptr
shared_ptr internal_ What's the use of Weaks?
Please see the following procedure:
shared_ptr<int> a(new int(1)); int* b = a.get(); shared_ptr<int> c(b); cout<<c.use_count();
Output: 1
shared_ptr simply wraps the pointer type and does not change any counters. So now we have two shared_ptr points to the same location, but_ Uses is 1.
This tells us one thing: try not to, or even never, mix smart pointers and pointers
Similar behavior, we can use weak_ptr.
weak_ptr is also derived from_ Ptr_base, so it can also output the memory it points to_ Uses, but weak_ptr does not increase it, but acts on it_ On the Weaks.
weak_ptr can output use_count(), you can use break_ PTR construction shared_ptr will not lead to abnormal behavior.