C++弱指针学习 —— The weak ptr Class Template

The std::tr1:shared_ptr class template is the big and famous sibling of another class template called std::tr1::weak_ptr. In this part I introduce weak_ptr, explain which programming problems it solves and how it is used.

To Have and to Own

shared_ptr differs from its nearly-extinct ancestor auto_ptr in one crucial aspect, namely resource sharing. auto_ptr implements "strict ownership", meaning it exclusively owns its bound resource. Thus, if two auto_ptrs own the same resource undefined behavior will occur. shared_ptr implements a different ownership strategy whereby multiple shared_ptr objects can safely own the same resource. This "sharing" is accomplished by reference counting. The counter is incremented every time a newshared_ptr object is bound to the same resource, and decremented whenever a shared_ptr object is destroyed. Only once the reference counter reaches zero is the shared resource released.

Statistically, I believe that the shared ownership implemented by shared_ptr is suitable for the majority of programming tasks. By now, shared_ptr is also the most widely-used standard smart pointer in C++. So why is weak_ptr necessary and how does it differ from shared_ptr?

Its Weakness is its Strength

weak_ptr offers a limited set of operations compared to shared_ptr because it’s not really a smart pointer in the classic sense. Smart pointers typically employ the RAII idiom. They lock a resource when they are constructed and release it during their destruction. Weak_ptr does neither of this. Instead, it stores a "weak reference" to an object that’s already managed by a shared_ptr. Unlike shared_ptr,weak_ptr doesn’t increment the reference counter of the shared resource. For example, if you have a shared_ptr and a weak_ptr that are both bound to the same resource, the reference count is 1, not 2:

A * pa = new A;
shared_ptr <A> sp1(pa); //reference count of pa is 1
weak_ptr<A> wp1(sp1); ////reference count of pa is 1
shared_ptr <A> sp2(sp1); //reference count of pa is 2
cout<< "use count is: "<<sp.use_count()<<endl; //2

理解:从类的角度来说,弱指针并不是智能指针。智能指针采用RAII技术,他们在构造的时候锁上一个资源,在析构的时候释放一个资源,而弱指针不会做这些,相反,它只是

Cyclic Dependency

weak_ptr objects are used for breaking cycles in data structures. A cycle is similar to a deadlock in multithreading: two resources hold pointers to one another so pointer a cannot be released because it’s still shared by a resource owned by pointer b. Pointer b cannot be released either because its resource is owned by pointer a. You can break such cyclic dependency by using weak_ptr instead ofshared_ptr. A linked list data structure demonstrates this problem:

struct Node
{
shared_ptr<Node> next;
};
Node * phead= new Node;
Node *pn1 = new Node;
shared_ptr<Node> root(phead);
phead->next=shared_ptr <Node> (pn1);
pn1->next=root; //cycle

Let’s see how the use of weak_ptr solves this problem. First, you need to modify the Node data structure:

struct Node
{
shared_ptr<Node> next;
weak_ptr<Node> w_next;
};

Next, replace pn1-next with pn1-w_next:

Node * phead= new Node;
Node *pn1 = new Node;
shared_ptr<Node> root(phead);
phead->next=shared_ptr <Node> (pn1);
pn1->w_next=root; // no cycle

Because weak_ptr doesn’t increment or decrement the use count, it can’t own a resource that isn’t already owned by a shared_ptr object. Furthermore, a weak_ptr object might expire while it’s still alive. This can happen when the last shared_ptr object has been destroyed and released the resource. In this situation, a weak_ptr object pointing to the released resource is said to have been expired. For this reason, you can’t access the owned resource directly from a weak_ptr object. Instead, you have to construct a shared_ptr object from that weak_ptr object and access the resource through thatshared_ptr object. There are two ways to do it:

constructing a shared_ptr with a weak_ptr argument. This technique is shown here:

shared_ptr<A> func(const weak_ptr<A>& wpa)
{
if (!wpa.expired())
 return shared_ptr<wpa);
return shared_ptr<A>; //empty shared_ptr
}

Always ensure that the weak_ptr hasn’t expired before you try to construct a shared_ptr from it. Constructing a shared_ptr from an expired weak_ptr throws std::tr1::bad_weak_ptr exception.

Using the weak_ptr::lock() member function. This technique doesn’t throw. lock() returns an emptyshared_ptr if *this has expired. Otherwise, it returns a shared_ptr object that owns the resource to which *this is pointing (you may have noticed that lock() does what func() does).

In addition to expire(), weak_ptr includes the use_count() member function which reports how manyshared_ptr objects own the resource to which *this is pointing. If wp.use_count() is higher than zero, it means that wp hasn’t expired. Technically speaking, expire() isn’t really necessary because you can obtain the same information by calling use_count(). However, some implementers reports that use_count() is in some cases less efficient than expire() so you should use the latter when possible.

In the second part of this series I will discuss additional properties of weak_ptr, including assignment, swapping, reset() and comparing weak_ptr objects.

posted on 2015-12-24 10:25  gyt929458988  阅读(966)  评论(0)    收藏  举报