HandBooks Professional Java-C-Scrip-SQL part 42 ppsx

6 112 0
HandBooks Professional Java-C-Scrip-SQL part 42 ppsx

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

Thông tin tài liệu

public: some_class() { std::cout << "some_class::some_class()\n"; } some_class(const some_class& other) { std::cout << "some_class(const some_class& other)\n"; } ~some_class() { std::cout << "some_class::~some_class()\n"; } }; int main() { std::cout << "Before start of scope\n"; { boost::intrusive_ptr<some_class> p1(new some_class()); boost::intrusive_ptr<some_class> p2(p1); } std::cout << "After end of scope \n"; } To demonstrate that the intrusive_ptrs together with the functions intrusive_ptr_add_ref and intrusive_ptr_release do their jobs right, here is the output from running the program: Before start of scope some_class::some_class() some_class::~some_class() After end of scope The intrusive_ptr is taking care of business for us. When the first intrusive_ptr p1 is created, it is passed a new instance of some_class. The intrusive_ptr constructor actually takes two arguments. The second is a bool that states whether intrusive_ptr_add_ref should be called or not. Because the default value of this argument is TRue, when constructing p1, the reference counter for the instance of some_class becomes 1. Then, a second intrusive_ptr, p2, is constructed. It is copy constructed from p1, and when p2 sees that p1 is referencing a non-null pointer, it calls intrusive_ptr_add_ref. The reference count is now 2. Then, the two intrusive_ptrs leave scope. First, p2 is destroyed, and the destructor calls intrusive_ptr_release. This decrements the reference counter to 1. Then, p1 is destroyed, and the destructor calls intrusive_ptr_release again, which causes the reference count to drop to 0; this in turn triggers our implementation of intrusive_ptr_release to delete the pointer. You'll note that the implementation of reference_counter is not thread-safe, and therefore cannot be used in multithreaded applications without adding synchronization. Rather than relying on a generic implementation of intrusive_ptr_add_ref and intrusive_ptr_release, we could have these functions operate directly on the base class (here, reference_counter). The advantage of this approach is that even if the classes derived from reference_counter are defined in other namespaces, intrusive_ptr_add_ref and intrusive_ptr_release will still be found using ADL (argument dependent lookup). Changing the implementation of reference_counter is straightforward. class reference_counter { int ref_count_; public: reference_counter() : ref_count_(0) {} virtual ~reference_counter() {} friend void intrusive_ptr_add_ref(reference_counter* p) { ++p->ref_count_; } friend void intrusive_ptr_release(reference_counter* p) { if ( p->ref_count_==0) delete p; } protected: reference_counter& operator=(const reference_counter&) { // No-op return *this; } private: // Copy construction disallowed reference_counter(const reference_counter&); }; Treating this As a Smart Pointer It's not altogether easy to come up with scenarios where intrusive, reference- counted smart pointers are really required. Most, if not all, problems can be solved with non-intrusive smart pointers. However, there is one case in which it's easier to use an intrusive reference count: when one needs to return this from a member function, to be stored in another smart pointer. When returning this from a type that's being owned by non-intrusive smart pointers, the result is that two different smart pointers believe that they own the same object, which implies that they will both try to delete it when the time has come to do so. This leads to double deletion, with the probable result that your application will crash. It must somehow be possible to tell the other smart pointer that this resource is already referenced by another smart pointer, and that's exactly what an internal reference counter (implicitly) does. Because the logic of intrusive_ptr indirectly operates on the internal reference count of the objects they refer to, there is no violation of ownership or inconsistencies in the reference counting. The reference count is simply incremented. Let's take a look at the potential problem first, with an implementation relying on boost::shared_ptr for sharing resource ownership. It's basically the example from earlier in this chapter, when discussing enable_shared_from_this. #include "boost/shared_ptr.hpp" class A; void do_stuff(boost::shared_ptr<A> p) { // } class A { public: call_do_stuff() { shared_ptr<A> p(???); do_stuff(p); } }; int main() { boost::shared_ptr<A> p(new A()); p->call_do_stuff(); } The class A wants to call the function do_stuff, but the problem is that do_stuff expects a shared_ptr<A>, not a plain pointer to A. So, in A::call_do_stuff, how should the shared_ptr be created? Now, let's rewrite A to make it compatible with intrusive_ptr, by deriving from reference_counter, and let's also add an overloaded version of do_stuff, accepting an argument of type intrusive_ptr<A>. #include "boost/intrusive_ptr.hpp" class A; void do_stuff(boost::intrusive_ptr<A> p) { // } void do_stuff(boost::shared_ptr<A> p) { // } class A : public reference_counter { public: void call_do_stuff() { do_stuff(this); } }; int main() { boost::intrusive_ptr<A> p(new A()); p->call_do_stuff(); } As you can see, in this version of A::call_do_stuff, we are able to send this directly to the function expecting an intrusive_ptr<A>, due to the converting constructor of intrusive_ptr. Here's a special treat to end this section: Now that A is supporting intrusive_ptr, we can actually write code that creates a shared_ptr that wraps the intrusive_ptr, allowing us to call the original version of do_stuff, which takes a shared_ptr<A> as argument. Assuming that you cannot control the source code for do_stuff, this might be a very real problem that you need to solve. Again, the solution awaits in the form of a custom deleter, one that understands that it needs to call intrusive_ptr_release. Here's the new version of A::call_do_stuff. void call_do_stuff() { intrusive_ptr_add_ref(this); boost::shared_ptr<A> p(this,&intrusive_ptr_release<A>); do_stuff(p); } An elegant solution indeed. When there are no more shared_ptrs left, the custom deleter is invoked, which calls intrusive_ptr_release, which in turn decreases the internal reference counter of A. Note that if intrusive_ptr_add_ref and intrusive_ptr_release are implemented to operate on reference_counter, you'd create the shared_ptr like so: boost::shared_ptr<A> p(this,&intrusive_ptr_release); Supporting Different Reference Counters We talked earlier about the possibility of supporting different reference counts for different types. This may be necessary when integrating existing classes with different reference-counting mechanisms (third-party classes employing their own version of a reference-counter, for example). Or there may be different requirements for deallocating, such as calling another function rather than delete. As mentioned already, the calls to intrusive_ptr_add_ref and intrusive_ptr_release are unqualified. This means that the scope of the argument (the pointer type) is considered during name lookup, and thus these functions should be defined in the same scope as the type on which they should operate. If you implement generic versions of intrusive_ptr_add_ref and intrusive_ptr_release in the global namespace, you make it impossible to create generic versions in other namespaces. For example, if a namespace needs a special version for all of its types, specializations or overloads must be provided for each and every type. Otherwise, the functions in the global namespace introduce an ambiguity. It is therefore not a good idea to provide generic versions in the global namespace, though they are fine in other namespaces. Because of the way that we have implemented the reference counter, using the base class reference_counter, it is a good idea to have an ordinary function in the global namespace that accepts an argument of type reference_counter*. This still allows us to provide generic overloads inside other namespaces without introducing ambiguities. As an example, consider the classes another_class and derived_class in a namespace called my_namespace. namespace my_namespace { class another_class : public reference_counter { public: void call_before_destruction() const { std::cout << "Yes, I'm ready before destruction\n"; } }; . necessary when integrating existing classes with different reference-counting mechanisms (third-party classes employing their own version of a reference-counter, for example). Or there may be

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

Từ khóa liên quan

Tài liệu cùng người dùng

  • Đang cập nhật ...

Tài liệu liên quan