Detailed C ++ 11 smart pointer

Foreword

Inside the four C ++ smart pointer: auto_ptr, unique_ptr, shared_ptr, weak_ptr where three is the C ++ supports 11, 11 and the first has been abandoned C ++.

C ++ 11 introduces smart pointer

Smart pointers primarily used to manage the heap memory allocated, it is a common package stack pointer object. When the end of the life cycle of the stack of objects, the application will be freed in memory destructor, to prevent a memory leak. C ++ 11 most commonly used is the shared_ptr smart pointer type, which uses a reference counting method, the current record number is referenced memory resources smart pointer. The reference count is allocated on the heap memory. Plus 1, decrements the reference count when a new one when the reference count expires. Only when the reference count is 0, the smart pointer will automatically release the memory resource reference. You can not assign an ordinary pointer when initialized directly to shared_ptr smart pointer as a pointer, a class is. It may be passed through an ordinary pointer make_shared the constructor or function. And can be obtained by an ordinary pointer get function.

Why use a smart pointer

The role of smart pointer is a pointer management, because there is such a case: space applications forget to release at the end of the function, resulting in a memory leak. Use smart pointers can largely avoid this problem, because the smart pointer is a class when the class is beyond the scope of that class will automatically call the destructor, the destructor will automatically release resources. So the role of the principle of smart pointer is automatically free up memory space at the end of the function, without having to manually release the memory space.

auto_ptr

(C ++ 98 programs, C ++ 11 has abandoned) using proprietary mode.

auto_ptr<string> p1 (new string ("I reigned lonely as a cloud.”)); 
auto_ptr<string> p2; 
p2 = p1; //auto_ptr不会报错.

At this point does not complain, p2 deprived of the ownership of p1, p1 but access will be thrown when the program runs. So auto_ptr drawback is: there is a potential memory corruption problem!

unique_ptr

(Replace auto_ptr) unique_ptr implement strict owns or has exclusive concepts, ensure that only one smart pointer can point to the object in the same time. It is essential to avoid resource leaks (for example, "After creating new objects because an exception occurs and forget to call delete") is particularly useful.

Examples of the use of the ownership pattern, or above

unique_ptr<string> p3 (new string ("auto"));   //#4
unique_ptr<string> p4;                       //#5
p4 = p3;//此时会报错!!

The compiler think p4 = p3 illegally, avoiding the problem p3 no longer point to valid data. It will compile error when you try to copy p3, and auto_ptr can compile error which buried a hidden danger at runtime. Therefore, unique_ptr more secure than auto_ptr.

In addition there are smarter places unique_ptr: When a program tries to unique_ptr assigned to another, if the source is a temporary unique_ptr the right values, the compiler allowed to do so; if the source unique_ptr will exist for some time, the compiler will be prohibited to do so, such as:

unique_ptr<string> pu1(new string ("hello world")); 
unique_ptr<string> pu2; 
pu2 = pu1;                                      // #1 不允许
unique_ptr<string> pu3; 
pu3 = unique_ptr<string>(new string ("You"));   // #2 允许

Where # 1 left hanging unique_ptr (pu1), which could lead to harm. And # 2 will not leave hanging unique_ptr, as it is calling the constructor unique_ptr, the constructor creates temporary objects will be destroyed after its ownership give pu3. This situation with the behavior at the show, unique_ptr than allow the auto_ptr two assignment.

NOTE: If you really want to perform the operation similar to # 1, to secure the re-use such pointers can assign it a new value. There is a standard C ++ library function std :: move (), allows you to assign a unique_ptr another. Despite the transfer of ownership is still possible to the original situation pointer calls (calls to collapse) appears. But the grammar you are able to emphasize the transfer of ownership, so that you clearly know what they are doing, so as not to invoke chaos original pointer.

( Extra: the Boost :: scoped_ptr the Boost library is a exclusive smart pointer, but it does not allow the transfer of ownership from the beginning and the end are only responsible for a resource, it is more secure cautious, but also narrower range of applications.)

E.g:

unique_ptr<string> ps1, ps2;
ps1 = demo("hello");
ps2 = move(ps1);
ps1 = demo("alexia");
cout << *ps2 << *ps1 << endl;

shared_ptr

shared_ptr have to achieve shared concept. Multiple smart pointers can point to the same object and its associated resources will "be destroyed last reference" when released. Share can be seen from the name of the resource may be shared by multiple pointers, using the counting mechanism to indicate that resources are shared several pointers. You can see the number of the owner of the resource by the member function use_count (). In addition to be constructed by new, can be constructed by passing auto_ptr, unique_ptr, weak_ptr. When we call to release (), the current pointer will release resources ownership, count by one. When the count is equal to 0, the resource is released.

shared_ptr is to address the limitations on the ownership of an object auto_ptr (auto_ptr is exclusive), provides a smart pointer to share ownership in the mechanism uses reference counting.

Member function:

Returns the number of the reference count use_count

unique Returns whether the exclusive owner (use_count 1)

for swap two shared_ptr objects (i.e., objects owned by exchange)

Reduce internal reset to give up ownership of the object or the object has changed, it will cause the original object's reference count

Returns the internal get objects (pointers), because they have a heavy load () method, and so direct use of the object is the same as

shared_ptr<int> sp(new int(1)); 

sp and sp.get () are equivalent.

share_ptr simple example:

int main()
{
    string *s1 = new string("s1");

    shared_ptr<string> ps1(s1);
    shared_ptr<string> ps2;
    ps2 = ps1;

    cout << ps1.use_count()<<endl;  //2
    cout<<ps2.use_count()<<endl;    //2
    cout << ps1.unique()<<endl; //0

    string *s3 = new string("s3");
    shared_ptr<string> ps3(s3);

    cout << (ps1.get()) << endl;    //033AEB48
    cout << ps3.get() << endl;  //033B2C50
    swap(ps1, ps3); //交换所拥有的对象
    cout << (ps1.get())<<endl;  //033B2C50
    cout << ps3.get() << endl;  //033AEB48

    cout << ps1.use_count()<<endl;  //1
    cout << ps2.use_count() << endl;    //2
    ps2 = ps1;
    cout << ps1.use_count()<<endl;  //2
    cout << ps2.use_count() << endl;    //2
    ps1.reset();    //放弃ps1的拥有权,引用计数的减少
    cout << ps1.use_count()<<endl;  //0
    cout << ps2.use_count()<<endl;  //1
}

weak_ptr

share_ptr Although it has been well spent, but one thing share_ptr smart pointer still have a memory leak case, when two objects with each other using a shared_ptr member variable pointing at each other, will result in a circular reference, the reference count failure, resulting in a memory leak.

weak_ptr not control is a smart pointer object life cycle, it points to an object managed by shared_ptr. memory management of the object that is referenced by strong shared_ptr, weak_ptr only provides a means of access to managed objects. Weak_ptr purpose designed with a smart pointer is introduced to assist shared_ptr shared_ptr work, it can only be from one or the other weak_ptr shared_ptr object construction, its construction and destructor does not cause increase in the number of reference mark or reduced. weak_ptr is used to solve the deadlock when shared_ptr refer to each other, if two shared_ptr refer to each other, then the two pointer reference count will never drop to 0, the resource is never released. It is a weak reference object, the object will not increase the reference count between, and can be transformed into each other shared_ptr, shared_ptr can be assigned directly to it, it can be obtained by calling the shared_ptr lock function.

class B;    //声明
class A
{
public:
    shared_ptr<B> pb_;
    ~A()
    {
        cout << "A delete\n";
    }
};

class B
{
public:
    shared_ptr<A> pa_;
    ~B()
    {
        cout << "B delete\n";
    }
};

void fun()
{
    shared_ptr<B> pb(new B());
    shared_ptr<A> pa(new A());
    cout << pb.use_count() << endl; //1
    cout << pa.use_count() << endl; //1
    pb->pa_ = pa;
    pa->pb_ = pb;
    cout << pb.use_count() << endl; //2
    cout << pa.use_count() << endl; //2
}

int main()
{
    fun();
    return 0;
}

See fun function pa, references to each other pb, two resource reference count is 2, when a jump to the function, the smart pointer pa, pb destructor when two resource reference count is decremented by one, but both cited or for 1 count, resulting in resources are not released (a, B is not the destructor to be called) is not running out of a function results destructor content output, resulting in a memory leak. If one instead weak_ptr on it, we put inside the class A shared_ptr pb_, instead weak_ptr pb_, results are as follows:

1
1
1
2
B delete
A delete

In this case, the reference resource B 1 starts only when pb destructor, the count becomes B 0, B is released, and also make the release of B minus A count of 1, while when A destructor pa count by 1, the count a is 0, a is released.

Note: we can not weak_ptr direct access to an object method, the object B such as a method Print (), so we can not access, pa-> pb _-> print ( ), since a pb_ weak_ptr, it should first conversion as shared_ptr , such as:

shared_ptr<B> p = pa->pb_.lock();
p->print();

The core share_ptr and implementation weak_ptr

weakptr as weak reference pointer, which depends on the realization of the counter and the counter share_ptr class assignment, configuration, and so the first counter share_ptr

Counter simple implementation

class Counter
{
public:
    Counter() : s(0), w(0){};
    int s;  //share_ptr的引用计数
    int w;  //weak_ptr的引用计数
};

head to the object counter is used to apply a piece of memory to keep the base reference, s is share_ptr reference count, the reference count is weak_ptr of w, when w is 0, the object delete Counter.

Simple implementation of share_ptr

template <class T>
class WeakPtr; //为了用weak_ptr的lock(),来生成share_ptr用,需要拷贝构造用

template <class T>
class SharePtr
{
public:
    SharePtr(T *p = 0) : _ptr(p)
    {
        cnt = new Counter();
        if (p)
            cnt->s = 1;
        cout << "in construct " << cnt->s << endl;
    }
    ~SharePtr()
    {
        release();
    }

    SharePtr(SharePtr<T> const &s)
    {
        cout << "in copy con" << endl;
        _ptr = s._ptr;
        (s.cnt)->s++;
        cout << "copy construct" << (s.cnt)->s << endl;
        cnt = s.cnt;
    }
    SharePtr(WeakPtr<T> const &w) //为了用weak_ptr的lock(),来生成share_ptr用,需要拷贝构造用
    {
        cout << "in w copy con " << endl;
        _ptr = w._ptr;
        (w.cnt)->s++;
        cout << "copy w  construct" << (w.cnt)->s << endl;
        cnt = w.cnt;
    }
    SharePtr<T> &operator=(SharePtr<T> &s)
    {
        if (this != &s)
        {
            release();
            (s.cnt)->s++;
            cout << "assign construct " << (s.cnt)->s << endl;
            cnt = s.cnt;
            _ptr = s._ptr;
        }
        return *this;
    }
    T &operator*()
    {
        return *_ptr;
    }
    T *operator->()
    {
        return _ptr;
    }
    friend class WeakPtr<T>; //方便weak_ptr与share_ptr设置引用计数和赋值

protected:
    void release()
    {
        cnt->s--;
        cout << "release " << cnt->s << endl;
        if (cnt->s < 1)
        {
            delete _ptr;
            if (cnt->w < 1)
            {
                delete cnt;
                cnt = NULL;
            }
        }
    }

private:
    T *_ptr;
    Counter *cnt;
};

Share_ptr given function interface as: configuration, copy constructor, assignment dereferencing, by the release and delete _ptr memory cnt is 0 when the reference count.

weak_ptr simple implementation

template <class T>
class WeakPtr
{
public: //给出默认构造和拷贝构造,其中拷贝构造不能有从原始指针进行构造
    WeakPtr()
    {
        _ptr = 0;
        cnt = 0;
    }
    WeakPtr(SharePtr<T> &s) : _ptr(s._ptr), cnt(s.cnt)
    {
        cout << "w con s" << endl;
        cnt->w++;
    }
    WeakPtr(WeakPtr<T> &w) : _ptr(w._ptr), cnt(w.cnt)
    {
        cnt->w++;
    }
    ~WeakPtr()
    {
        release();
    }
    WeakPtr<T> &operator=(WeakPtr<T> &w)
    {
        if (this != &w)
        {
            release();
            cnt = w.cnt;
            cnt->w++;
            _ptr = w._ptr;
        }
        return *this;
    }
    WeakPtr<T> &operator=(SharePtr<T> &s)
    {
        cout << "w = s" << endl;
        release();
        cnt = s.cnt;
        cnt->w++;
        _ptr = s._ptr;
        return *this;
    }
    SharePtr<T> lock()
    {
        return SharePtr<T>(*this);
    }
    bool expired()
    {
        if (cnt)
        {
            if (cnt->s > 0)
            {
                cout << "empty " << cnt->s << endl;
                return false;
            }
        }
        return true;
    }
    friend class SharePtr<T>; //方便weak_ptr与share_ptr设置引用计数和赋值
    
protected:
    void release()
    {
        if (cnt)
        {
            cnt->w--;
            cout << "weakptr release" << cnt->w << endl;
            if (cnt->w < 1 && cnt->s < 1)
            {
                //delete cnt;
                cnt = NULL;
            }
        }
    }

private:
        T *_ptr;
    Counter *cnt;
};

weak_ptr generally configured by share_ptr, expired by checking the original function pointer is empty, lock transformed into share_ptr.

Guess you like

Origin www.cnblogs.com/WindSun/p/11444429.html
Recommended