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

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

6 113 0
Tài liệu đã được kiểm tra trùng lặp

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

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 6
Dung lượng 21,76 KB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

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_rele

Trang 1

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

Trang 2

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&);

};

Trang 3

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();

}

Trang 4

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

Trang 5

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

Trang 6

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

}

};

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