Valid tive shared_ptr

Source: Internet
Author: User

0. Preface

This article is the second part of the C ++ smart pointer class, but the article cannot be longer, so I have to write it into an article separately, and write the circular reference of shared_ptr here, which is a little more coherent.

1. circular reference of shared_ptr


DefinitionThe so-called circular reference is similar to a tree like this. It contains a pointer from the parent node to the child node, and a pointer from the child node to the father node, that is, the parent node and the child node reference each other.

Let's take a look at an example (adapted from: the dead point of the smart pointer --- circular reference ):

#include <iostream>#include <memory>using namespace std;class B;class A{public:    A(){cout<<"A constructor"<<endl;}    ~A(){cout<<"A destructor"<<endl;}    shared_ptr<B> m_b;};class B{public:    shared_ptr<A> m_a;    B(){cout<<"B constructor"<<endl;}    ~B(){cout<<"B destructor"<<endl;}};int main(){    cout<<"shared_ptr cycle reference:\n";    shared_ptr<A> a(new A);    shared_ptr<B> b(new B);    a->m_b = b; //cycle reference    b->m_a = a;    return 0;}

Output:

The output result shows that the Destructor A and B are not executed and the memory is leaked!

Analysis: As we all know, the new object must be deleted by the programmer, and a smart pointer: shared_ptr is used to point to new, that is to say, the delete responsibility falls on shared_ptr (when it exits the scope ). But the above Code is analyzed: B's first-out scope (the reverse structure order is the same as the structure), B's reference count is reduced to 1, not 0, so the space on the heap B is not released, now the result is: B is gone, but new B is not deleted. Okay, now you have to wait.
A to delete. Then a exits its scope, and the reference count of A is reduced to 1, not 0, because M_a in B points to it, and the result is: A is gone, but new A has not been deleted, and there are no uplo_ptr objects to delete them. No, it seems that they exist in new objects A and B, if there is no Delete, they will not go beyond the scope. They are waiting for the delete, while the delete is waiting for the shared_ptr object to issue the delete itself. The conflict arises, so the deadlock is like this !!! Therefore, the new A and B are abandoned and the memory is leaked.

Cause:(1)The new object must be manually deleted;(2)Master the shared_ptr of Delete in the new object;(3)The shared_ptr in two new objects waits for each other.

Unlock: If there is only one direction, the code above removes a line: B-> m_a = A;, but stores the information referenced by B a somewhere, the shared_ptr objects of A and B are invisible, but such information can observe the behavior of shared_ptr objects pointing to A and B. Next, let's analyze: B first goes out of scope, and B's reference count is reduced to 1, not 0. At this time, the space on B is not released, and the result is still: B is gone, but new B is not deleted.
Drop. Then a exits the scope. Note: At this time, the reference count of A is reduced to 0, and resource A is released. This also causes the shared_ptr object pointing to resource B in space A to be out of the scope, so that the reference count of B is reduced to 0 and B is released, so that both A and B can be correctly released. This should be the prototype of weak_ptr smart pointer.

Let's take a look at the original example (Added weak_ptr):

#include <iostream>#include <memory>using namespace std;class B;class A{public:    A(){cout<<"A constructor"<<endl;}    ~A(){cout<<"A destructor"<<endl;}    shared_ptr<B> m_b;};class B{public:    weak_ptr<A> m_a;    B(){cout<<"B constructor"<<endl;}    ~B(){cout<<"B destructor"<<endl;}};int main(){    cout<<"shared_ptr cycle reference:\n";    shared_ptr<A> a(new A);    shared_ptr<B> b(new B);    cout<<"a counter: "<<a.use_count()<<endl;    cout<<"b counter: "<<b.use_count()<<endl;    a->m_b = b;     b->m_a = a;    cout<<"a counter: "<<a.use_count()<<endl;    cout<<"b counter: "<<b.use_count()<<endl;    cout<<"b->m_a counter: "<<b->m_a.use_count()<<endl; //that is the reference counts of A    cout<<"expired: "<<std::boolalpha<<b->m_a.expired()<<endl;    return 0;}

Output:

It can be seen that both A and B have successfully analyzed the structure.

Ii. Repeated analysis structure of shared_ptr

The word "Repeat structure" in shared_ptr is actually a bit strange, because mongo_ptr is not proposed due to the memory leakage and repeated structure possible by the normal pointer (raw pointer, how can we make the same mistake?

The reason is thatIn many cases, shared_ptr is not fully used, but is a mix of common pointers and smart pointers. Or, this situation is concealed, which may lead to repeated analysis.

Scenario 1 --- simple mashups:

int* pInt = new int(10);shared_ptr<int> sp1(pInt);...shared_ptr<int>sp2(pInt);

It is interpreted by the shared_ptr constructor and its source code (for the source code of shared_ptr, see std: tr1: shared_ptr and shared_ptr ):

//constructortemplate<class T>explicit shared_ptr(T* ptr);...//tr1::shared_ptr   source code...public:    shared_ptr(T* p = NULL)    {         m_ptr = p;         m_count = new sp_counter_base(1, 1);         _sp_set_shared_from_this(this, m_ptr);       }...

Based on the source code of shared_ptrYes: In this case, shared_ptr (including reference count and control block) constructed by a common pointer will generate a reference Count class (new sp_counter_base (1, 1)
) The reference count is initialized to 1. If there is another constructor of this class (for the same common pointer), a reference counting class will be re-constructed, and the reference count is initialized to 1 (instead of adding 1 to 2 ). In this way, two shared_ptr objects will be mistaken, resulting in repeated analysis later.

Scenario 2 --- mix and match with this pointer

#include <iostream>#include <memory>using namespace std;class A{private:public:    A(){cout<<"constructor"<<endl;}    ~A(){cout<<"destructor"<<endl;}    shared_ptr<A> sget()    {        shared_ptr<A> sp(this);        cout<<"this: "<<this<<endl;        return sp;    }};int main(){    shared_ptr<A> test (new A);    shared_ptr<A> spa = test->sget();    cout<<"spa: "<<spa<<endl;    cout<<"test: "<<test<<endl;    cout<<"spa counter: "<<spa.use_count()<<endl;    cout<<"test counter: "<<test.use_count()<<endl;    return 0;}


Output
:

The program displays [core dumped], based on the information before the program crashYes:

The structure of object A is parsed twice,The reason is thatThe temporary shared_ptr object SP in the sget () function is constructed by the common pointer this. Therefore, the generated shared_ptr object will generate a new reference counting class (different from the test class ), and the initialization count is 1. This will cause both test and SPA to execute the destructor when they exit their respective scopes.

Solution: The enable_from_shared_this class is provided in C ++ 11. Other classes can inherit it and use the shared_from_this method to obtain the shared_ptr smart pointer of class objects, in this case, the reference counting class is the same (the specific implementation is related to the weak_ptr class. For details, see shared_from_this source code ).

(1) Let a inherit the enable_from_shared_this class

(2) modify the sget function and call the shared_from_this method to obtain the shared_ptr version of the Class Object.

#include <iostream>#include <memory>using namespace std;class A :public enable_shared_from_this<A>{private:public:    A(){cout<<"constructor"<<endl;}    ~A(){cout<<"destructor"<<endl;}    shared_ptr<A> sget()    {        return shared_from_this();    }};int main(){    shared_ptr<A> test (new A);    shared_ptr<A> spa = test->sget();    cout<<"spa: "<<spa<<endl;    cout<<"test: "<<test<<endl;    cout<<"spa counter: "<<spa.use_count()<<endl;    cout<<"test counter: "<<test.use_count()<<endl;    return 0;}

Output:

At this time, the structure is analyzed only once, and the reference count of test and spa is the same reference Count class, the value is 2.

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.