Tải bản đầy đủ (.pdf) (6 trang)

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

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (21.76 KB, 6 trang )

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";
}
};

×