Boost smart pointer :scoped_ptr shared_ptr weak_ptr

  • 2020-04-02 01:30:24
  • OfStack

A scoped_ptr.
Boost ::scoped_ptr is very similar to STD ::auto_ptr, and is a simple smart pointer that ensures that objects are automatically released when they leave scope. The following code illustrates the basic application of this pointer:


#include <string>
#include <iostream>
#include <boost/scoped_ptr.hpp>
class implementation
{
public:
    ~implementation() { std::cout <<"destroying implementationn"; }
    void do_something() { std::cout << "did somethingn"; }
};
void test()
{
    boost::scoped_ptr<implementation> impl(new implementation());
    impl->do_something();
}
void main()
{
    std::cout<<"Test Begin ... n";
    test();
    std::cout<<"Test End.n";
}

The output of this code is:

Test Begin ...
did something
destroying implementation
Test End.

As you can see, the implementation class is automatically deleted when it leaves the impl scope, thus avoiding the memory leak caused by forgetting to call delete manually.

Boost: : scoped_ptr features:
Boost ::scoped_ptr is implemented very similarly to STD ::auto_ptr in that it makes use of objects on a stack to manage objects on a heap so that objects on the stack are automatically deleted when they are destroyed. The difference is that boost::scoped_ptr has stricter usage limits -- it can't be copied. This means that the: boost::scoped_ptr pointer cannot be converted.

1. No transfer of ownership
Boost ::scoped_ptr manages an object whose lifecycle is limited to one interval (between the '{}' that pointer is in) and cannot be passed outside of that interval, which means that boost::scoped_ptr objects cannot be returned as functions (STD ::auto_ptr can).

2. No Shared ownership
This is similar to STD ::auto_ptr. This feature makes the pointer easy to use on the one hand. On the other hand, it also makes functionality weak -- it can't be used in STL containers.

3. Cannot be used to manage array objects
Since boost::scoped_ptr deletes managed objects by delete, and array objects must be deleted by deletep[], boost::scoped_ptr cannot manage array objects, using the boost::scoped_array class.

Common operations for boost::scoped_ptr:
It can be simplified into the following form:


namespace boost {
    template<typename T> class scoped_ptr : noncopyable {
    public:
        explicit scoped_ptr(T* p = 0); 
        ~scoped_ptr(); 
        void reset(T* p = 0); 
        T& operator*() const; 
        T* operator->() const; 
        T* get() const; 
        void swap(scoped_ptr& b); 
    };
    template<typename T> 
    void swap(scoped_ptr<T> & a, scoped_ptr<T> & b); 
}

Its common operations are as follows:

A member function

function

The operator * ()

Access the members of the managed object by reference

The operator - > (a)

Access the members of the managed object as Pointers

The get ()

Release the managed object and manage another object

Swap (scoped_ptr & b)

Swap two objects managed by boost::scoped_ptr



The following test code demonstrates the basic use of these functions.

#include <string>
#include <iostream>
#include <boost/scoped_ptr.hpp>
#include <boost/scoped_array.hpp>
#include <boost/config.hpp>
#include <boost/detail/lightweight_test.hpp>
void test()
{
    // test scoped_ptr with a built-in type
    long * lp = new long;
    boost::scoped_ptr<long> sp ( lp );
    BOOST_TEST( sp.get() == lp );
    BOOST_TEST( lp == sp.get() );
    BOOST_TEST( &*sp == lp );
    *sp = 1234568901L;
    BOOST_TEST( *sp == 1234568901L );
    BOOST_TEST( *lp == 1234568901L );
    long * lp2 = new long;
    boost::scoped_ptr<long> sp2 ( lp2 );
    sp.swap(sp2);
    BOOST_TEST( sp.get() == lp2 );
    BOOST_TEST( sp2.get() == lp );
    sp.reset(NULL);
    BOOST_TEST( sp.get() == NULL );
}
void main()
{
    test();
}

Boost ::scoped_ptr and STD ::auto_ptr
Boost ::scoped_ptr and STD ::auto_ptr are very similar in function and operation, and how you choose between them depends on whether you want to transfer ownership of the managed object (for example, as the return value of the function). If this is not necessary, you can use boost::scoped_ptr to get the compiler to do more rigorous checking to find some incorrect assignments.

2. From
Boost ::scoped_ptr is simple to use, but its inability to share ownership limits its scope, a limitation that boost::shared_ptr can address. As the name implies, boost::shared_ptr is a smart pointer to Shared ownership, so let's first take a look at its basic usage with an example:


#include <string>
#include <iostream>
#include <boost/shared_ptr.hpp>
class implementation
{
public:
    ~implementation() { std::cout <<"destroying implementationn"; }
    void do_something() { std::cout << "did somethingn"; }
};
void test()
{
    boost::shared_ptr<implementation> sp1(new implementation());
    std::cout<<"The Sample now has "<<sp1.use_count()<<" referencesn";
    boost::shared_ptr<implementation> sp2 = sp1;
    std::cout<<"The Sample now has "<<sp2.use_count()<<" referencesn";

    sp1.reset();
    std::cout<<"After Reset sp1. The Sample now has "<<sp2.use_count()<<" referencesn";
    sp2.reset();
    std::cout<<"After Reset sp2.n";
}
void main()
{
    test();
}

The output of this program is as follows:

The Sample now has 1 references
The Sample now has 2 references
After Reset sp1. The Sample now has 1 references
destroying implementation
After Reset sp2.

As you can see, boost::shared_ptr Pointers sp1 and sp2 both have access to the implementation object, and the memory of the object they manage is automatically freed when both sp1 and sp2 release ownership of the object. At the same time, the memory of the Shared object is automatically managed.

Boost :: memory management mechanism for shared_ptr:
Boost ::shared_ptr's management mechanism is not complicated, it is to manage the object of the object to the reference count, when a new boost::shared_ptr to manage the object, it is to increase the reference count of the object by one; When the object is managed by a boost::shared_ptr, the reference count of the object is reduced by one. If the reference count of the object is zero, there is no pointer to manage it.

The above example can be illustrated as follows:

Sp1 manages the implementation object with a reference count of 1

2. Add sp2 to manage the implementation object and increase its reference count to 2

The implementation object is managed by sp1 release and its reference count is changed to 1

The implementation object is managed by sp2 release, its reference count is changed to 0, and the object is automatically deleted

Boost ::shared_ptr features:
In contrast to the previously described boost::scoped_ptr, boost::shared_ptr shares ownership of an object, so there are few restrictions on how much it can be used (there are a few rules to follow, described below), and it can also be used in STL containers. It's also thread-safe, which is important in multithreaded programs.

Boost ::shared_ptr
Boost ::shared_ptr is not foolproof. Here are a few rules to make it safer to use boost::shared_ptr:

1. Avoid direct memory management operations on an object managed by shared_ptr to avoid re-freeing the object
2. Shared_ptr does not automatically manage the memory of objects that are cycled (this is a common problem with various other methods of reference counting to manage memory).
3. Do not construct a temporary shared_ptr as an argument to a function.

The following code may cause a memory leak:


void test()
{
    foo(boost::shared_ptr<implementation>(new    implementation()),g());
}
 The correct usage is: 
void test()
{
    boost::shared_ptr<implementation> sp    (new implementation());
    foo(sp,g());
}

3.   weak_ptr

Circular reference:
Reference counting is a convenient memory-management mechanism, but it has one big drawback: it cannot manage objects that are cyclically referenced. A simple example is as follows:


#include <string>
#include <iostream>
#include <boost/shared_ptr.hpp>
#include <boost/weak_ptr.hpp>
class parent;
class children;
typedef boost::shared_ptr<parent> parent_ptr;
typedef boost::shared_ptr<children> children_ptr;
class parent
{
public:
    ~parent() { std::cout <<"destroying parentn"; }
public:
    children_ptr children;
};
class children
{
public:
    ~children() { std::cout <<"destroying childrenn"; }
public:
    parent_ptr parent;
};

void test()
{
    parent_ptr father(new parent());
    children_ptr son(new children);
    father->children = son;
    son->parent = father;
}
void main()
{
    std::cout<<"begin test...n";
    test();
    std::cout<<"end test.n";
}

Running the program shows that even after exiting the test function, the parent and children objects cannot be released automatically because they both reference each other, their reference counts are 1, and the two objects are no longer accessible. This led to the infamous memory leak in c++.

In general, there are three possible ways to break this circular reference:
1. Manually break the circular reference to release the object when only the last reference is left.
2. When the parent's lifetime exceeds the children's lifetime, the children USES a normal pointer to parent instead.
3. Break this circular reference with a smart pointer to a weak reference.

While all three methods work, both methods 1 and 2 require manual control by the programmer, which is cumbersome and error-prone. Here's a look at the third method and the weak-referenced smart pointer boost::weak_ptr in boost.

Strong and weak references
A strong reference exists when the referenced object is alive (that is, if there is at least one strong reference, the object cannot be released). Boost ::share_ptr is a strong reference.

In contrast, weak references do not necessarily exist when the referenced object is alive. Just a reference when it exists. A weak reference does not modify the object's reference count, which means that the weak reference does not manage the object's memory and is functionally similar to a normal pointer. A big difference, however, is that the weak reference can detect whether the managed object has been freed and thus avoid accessing illegal memory.


boost::weak_ptr
boost::weak_ptr<T> is boost Provides a smart pointer to a weak reference whose declaration can be simplified as follows: 
namespace boost {
    template<typename T> class weak_ptr {
    public:
        template <typename Y>
        weak_ptr(const shared_ptr<Y>& r);
        weak_ptr(const weak_ptr& r);
        ~weak_ptr();
        T* get() const; 
        bool expired() const; 
        shared_ptr<T> lock() const;
    }; 
}

As you can see, boost::weak_ptr must be converted from one boost::share_ptr or another boost::weak_ptr, which also indicates that the memory management of the object is done by the strongly referenced boost::share_ptr. Boost ::weak_ptr simply provides an access to the managed object.

Boost ::weak_ptr in addition to its basic access to the managed object (via the get() function), there are two commonly used functions: expired() to detect whether the managed object has been released; Lock () is used to get a strong reference pointer to the managed object.

The circular reference is broken by boost::weak_ptr
Because a weak reference does not change the reference count, it is similar to a normal pointer and can be dereferenced by simply using a weak reference on the side of a circular reference. For the example above, simply change the definition of children to the following to remove the circular reference:


class children
{
public:
    ~children() { std::cout <<"destroying childrenn"; }
public:
    boost::weak_ptr<parent> parent;
};

Finally it is worth mentioning that although by a weak reference pointer can effectively remove circular reference, but this way must be in the programmer can foresee there will be a circular reference to use, also can be said that this is just a compile time solution, if the program appeared in the process of running a circular reference, still can cause a memory leak. Therefore, don't assume that using smart Pointers will prevent memory leaks. After all, for C++, memory leaks are a huge headache for every programmer because there is no garbage collection.


Related articles: