1. Trang chủ
  2. » Công Nghệ Thông Tin

O''''Reilly Network For Information About''''s Book part 43 pdf

8 228 0

Đang tải... (xem toàn văn)

THÔNG TIN TÀI LIỆU

class derived_class : public another_class {}; template <typename T> void intrusive_ptr_add_ref(T* t) { t->add_ref(); } template <typename T> void intrusive_ptr_release(T* t) { if (t->release()<=0) { t->call_before_destruction(); delete t; } } } Here, we have implemented generic versions of intrusive_ptr_add_ref and intrusive_ptr_release. We must therefore remove the generic versions that we previously put in the global namespace, and replace them with non-templated versions accepting a pointer to reference_counter as their argument. Or, we could omit these functions from the global namespace altogether, to avoid cluttering it. For the two classes my_namespace::another_class and my_namespace::derived_class, the special version (which calls a member function call_before_destruction on its arguments) is called. Other types either have corresponding functions in the namespace where they are defined or use the version in the global namespace, if it exists. Here's a short program to illustrate how this works: int main() { boost::intrusive_ptr<my_namespace::another_class> p1(new my_namespace::another_class()); boost::intrusive_ptr<A> p2(new good_class()); boost::intrusive_ptr<my_namespace::derived_class> p3(new my_namespace::derived_class()); } First, the intrusive_ptr p1 is passed a new instance of my_namespace:: another_class. When resolving the call to intrusive_ptr_add_ref, the compiler finds the version in my_namespace, the namespace of the my_namespace:: another_class* argument. Thus, the generic function, which is provided for types in that namespace, is correctly invoked. This applies when finding intrusive_ptr_release, too. Then, the intrusive_ptr p2 is created and passed a pointer of type A (the one we created earlier). That type resides in the global namespace, so when the compiler tries to find the best match for the call to intrusive_ptr_add_ref, it finds only one, which is the version accepting an argument of type pointer to reference_counter (you'll recall that we had to remove the generic version from the global namespace). Because A inherits publicly from reference_counter, the implicit conversion succeeds and the correct call is made. Finally, the generic version in my_namespace is used for the class my_namespace::derived_class; this works exactly like the lookup for another_class. The important lesson here is that when implementing the function intrusive_ptr_add_ref and intrusive_ptr_release, they should always be defined in the namespace where the types they operate on exist. This makes perfect sense from a design perspective too, keeping related things together, and it helps ensure that the correct version is always called, regardless of whether there are several different implementations to choose from. Summary In most situations, you should not use boost::intrusive_ptr, because the functionality of shared ownership is readily available in boost::shared_ptr, and a non-intrusive smart pointer is more flexible than an intrusive smart pointer. However, there are times when one needs an intrusive reference count, perhaps for legacy code or for integration with third-party classes. When the need arises, intrusive_ptr fits the bill, with the same semantics as the other Boost smart pointer classes. By using another of the Boost smart pointers, you ensure a consistent interface for all smart pointer needs, be they intrusive or not. The reference count must be provided by the classes that are used with intrusive_ptr. intrusive_ptr manages the reference count by making unqualified calls to two functions, intrusive_ptr_add_ref and intrusive_ptr_release; these functions must properly manipulate the intrusive reference count for intrusive_ptrs to work correctly. For all cases where a reference count already exists in the types that are to be used with intrusive_ptr, enabling support for intrusive_ ptr is as easy as implementing those two functions. In some cases, it's possible to create parameterized versions of those functions, and have all types with intrusive reference counts use the same implementation of these functions. In most cases, the best place to declare the functions is in the same namespace as the types they support. Use intrusive_ptr when  You need to treat this as a smart pointer.  There is existing code that uses or provides an intrusive reference count.  It is imperative that the size of the smart pointer equals the size of a raw pointer. weak_ptr Header: "boost/weak_ptr.hpp" A weak_ptr is an observer of a shared_ptr. It does not interfere with the ownership of what a shared_ptr shares. When a shared_ptr that is being observed by a weak_ptr must release its resource, it sets the observing weak_ptr's pointer to null. That prevents the weak_ptr from holding a dangling pointer. Why would you need a weak_ptr? There are many situations where one needs to observe and use a shared resource without accepting ownership, such as to break cyclic dependencies, to observe a shared resource without assuming ownership of it, or to avoid dangling pointers. It's possible to construct a shared_ptr from a weak_ptr, thereby gaining access to the shared resource. This is a partial synopsis for weak_ptr, showing and then briefly discussing the most important functions. 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; }; } Members template <typename Y> weak_ptr(const shared_ptr<Y>& r); This constructor creates a weak_ptr from a shared_ptr, provided there is an implicit conversion from Y* to T*. The new weak_ptr is configured to observe the resource referred to by r. r's reference count remains unchanged. This implies that the resource referenced by r may be deleted despite the existence of the new weak_ptr referring to it. This constructor never throws. weak_ptr(const weak_ptr& r); The copy constructor makes the new weak_ptr observe the resource referenced by shared_ptr r. The reference count of the shared_ptr is unchanged. This constructor never throws. ~weak_ptr(); The weak_ptr destructor, similarly to the constructor, does not change the reference count. If needed, the destructor detaches *this as an observer for the shared resource. This destructor never throws. bool expired() const; Returns TRue if the observed resource has "expired," which means that it has been released. If the stored pointer is non-null, expired returns false. This function never throws. shared_ptr<T> lock() const Returns a shared_ptr that refers to the resource that this weak_ptr observes, if any. If there is no such pointer (that is, the weak_ptr refers to the null pointer), the resulting shared_ptr refers to the null pointer. Otherwise, the reference count for the resource referenced by the shared_ptr is incremented as usual. This function never throws. Usage We begin with an example that shows the basics of weak_ptrs, and especially demonstrates how they do not affect the reference counts. Out of necessity, the examples in this subsection include shared_ptrs as well, because a weak_ptr always needs to be used together with a shared_ptr. To use weak_ptr, include "boost/weak_ptr.hpp". #include "boost/shared_ptr.hpp" #include "boost/weak_ptr.hpp" #include <iostream> #include <cassert> class A {}; int main() { boost::weak_ptr<A> w; assert(w.expired()); { boost::shared_ptr<A> p(new A()); assert(p.use_count()==1); w=p; assert(p.use_count()==w.use_count()); assert(p.use_count()==1); // Create a shared_ptr from the weak_ptr boost::shared_ptr<A> p2(w); assert(p2==p); } assert(w.expired()); boost::shared_ptr<A> p3=w.lock(); assert(!p3); } The weak_ptr w is default constructed, which means that it initially isn't observing any resource. To test whether or not a weak_ptr is observing a live object, you use the function expired. To start observing, a weak_ptr must be assigned a shared_ptr. In the example, when shared_ptr p is assigned to weak_ptr w, it's asserted that the use countsthat is, the reference countof p and w are equal. Then, a shared_ptr is constructed from the weak_ptr, which is one of the ways to gain access to the shared resource from a weak_ptr. If the weak_ptr has expired when the shared_ptr is constructed, an exception of type boost::bad_weak_ptr is thrown by shared_ptr's constructor. Moving on, when the shared_ptr p goes out of scope, w is expired. When its member function lock is called to obtain a shared_ptrthe second way of gaining access to the shared resourcean empty shared_ptr is returned. Note that throughout the example, the weak_ptr had no effect on the reference count of the shared object. Unlike other smart pointers, weak_ptr doesn't provide access to the observed pointer with overloaded operator* and operator->. The reason for this is that all operations on the resource that the weak_ptr is observing must explicitly be made safe; it would be just too easy to inadvertently access an invalid pointer, as weak_ptrs do not affect the reference counter of the shared resource they observe. This is why you must pass the weak_ptr to shared_ptr's constructor, or obtain a shared_ptr by calling weak_ptr::lock. Both of these operations increase the reference count, so after the shared_ptr is created from a weak_ptr, it keeps the shared resource alive, ensuring that it will not be deallocated during the time we want to use it. A Common Question Because the ordering of smart pointers doesn't involve the actual values their pointers are pointing to but the pointer values, a common question with regard to using these smart pointers in Standard Library containers is how to use algorithms with the smart pointers; they typically need to access the values of the actual objects rather than their addresses. For example, how does one call std::sort and make it perform useful sorting? Actually, the problem is hardly any different from storing and operating on regular pointers in containers, but that fact is easily overlooked (probably because storing a raw pointer is so problematic that we tend to avoid doing it). Out of the box, there is no support for comparing the values of smart pointers, but that's easy to amend. A typical need is to use predicates that dereference the smart pointers, so we'll create a reusable predicate that makes it easy to use the Standard Library algorithms with iterators referencing smart pointersin this case, weak_ptrs. #include <functional> #include "boost/shared_ptr.hpp" #include "boost/weak_ptr.hpp" template <typename Func, typename T> struct weak_ptr_unary_t : public std::unary_function<boost::weak_ptr<T>,bool> { T t_; Func func_; weak_ptr_unary_t(const Func& func,const T& t) : t_(t),func_(func) {} bool operator()(boost::weak_ptr<T> arg) const { boost::shared_ptr<T> sp=arg.lock(); if (!sp) { return false; } return func_(*sp,t_); } }; template <typename Func, typename T> weak_ptr_unary_t<Func,T> weak_ptr_unary(const Func& func, const T& value) { return weak_ptr_unary_t<Func,T>(func,value); } The weak_ptr_unary_t function object is parameterized on the function to invoke, and the type of the argument. The fact that the function to invoke is stored in the function object makes the function object easy to use, which we shall see shortly. To make the predicate compatible with adapters, weak_ptr_unary_t derives from std::unary_function, which makes sure that the required typedefs are present (this is required in order for the Standard Library adaptors to work with such function objects). The real work is done in the function call operator, where a shared_ptr is created from the weak_ptr. This is necessary to ensure that the resource stays alive during the function call. Then, the function (or function object) is invoked, passing the argument (dereferenced, so that we get to the actual resource) and the stored value, which was passed in the constructor for weak_ptr_unary_t. This simple function object can now be used with any applicable algorithms. For convenience, we also defined a helper function, weak_ptr_unary, which deduces the types of the arguments and returns an appropriate function object. [14] Let's see how we can use this beast. [14] To make this type generally usable, a lot more programming would be required. #include <iostream> #include <string> #include <vector> #include <algorithm> #include "boost/shared_ptr.hpp" #include "boost/weak_ptr.hpp" int main() { using std::string; using std::vector; using boost::shared_ptr; using boost::weak_ptr; vector<weak_ptr<string> > vec; shared_ptr<string> sp1( new string("An example")); shared_ptr<string> sp2( new string("of using")); shared_ptr<string> sp3( new string("smart pointers and predicates")); vec.push_back(weak_ptr<string>(sp1)); vec.push_back(weak_ptr<string>(sp2)); vec.push_back(weak_ptr<string>(sp3)); vector<weak_ptr<string> >::iterator it=std::find_if(vec.begin(),vec.end(), . However, there are times when one needs an intrusive reference count, perhaps for legacy code or for integration with third-party classes. When the need arises, intrusive_ptr fits the bill, with. reference count for intrusive_ptrs to work correctly. For all cases where a reference count already exists in the types that are to be used with intrusive_ptr, enabling support for intrusive_. made. Finally, the generic version in my_namespace is used for the class my_namespace::derived_class; this works exactly like the lookup for another_class. The important lesson here is that when

Ngày đăng: 07/07/2014, 08:20

Xem thêm: O''''Reilly Network For Information About''''s Book part 43 pdf