您的位置:首页 > 其它

Boost 内存管理----智能指针

2014-03-03 10:00 519 查看
shared_ptr 指针

 转自:http://www.cnblogs.com/helloamigo/p/3575098.html

 

简介

     boost::shared_ptr是可以共享所有权的指针。如果有多个shared_ptr共同管理同一个对象时,只有这些shared_ptr全部与该对象脱离关系之后,被管理的对象才会被释放。通过下面这个例子先了解下shared_ptr的基本用法:

1 #include <iostream>
2 #include <string>
3 #include <boost/shared_ptr.hpp>
4
5 using namespace std;
6
7 class Book
8 {
9 private:
10     string name_;
11
12 public:
13     Book(string name) : name_(name)
14     {
15         cout << "Creating book " << name_ << " ..." << endl;
16     }
17
18     ~Book()
19     {
20         cout << "Destroying book " << name_ << " ..." << endl;
21     }
22 };
23
24 int main()
25 {
26     cout << "=====Main Begin=====" << endl;
27     {
28         boost::shared_ptr<Book> myBook(new Book("「1984」"));
29         cout << "[From myBook] The ref count of book is " << myBook.use_count() << ".\n" << endl;
30
31         boost::shared_ptr<Book> myBook1(myBook);
32         cout << "[From myBook] The ref count of book is " << myBook.use_count() << "." << endl;
33         cout << "[From myBook1] The ref count of book is " << myBook1.use_count() << ".\n" << endl;
34
35         cout << "Reset for 1th time. Begin..." << endl;
36         myBook.reset();
37         cout << "[From myBook] The ref count of book is " << myBook.use_count() << "." << endl;
38         cout << "[From myBook1] The ref count of book is " << myBook1.use_count() << "." << endl;
39         cout << "Reset for 1th time. End ...\n" << endl;
40
41         cout << "Reset for 2th time. Begin ..." << endl;
42         myBook1.reset();
43         cout << "Reset for 2th time. End ..." << endl;
44     }
45     cout << "===== Main End =====" << endl;
46
47     return 0;
48 }


     运行结果:



     运行过程分析:

     shared_ptr的管理机制其实并不复杂,就是对所管理的对象进行了引用计数,当新增一个shared_ptr对该对象进行管理时,就将该对象的引用计数加一;减少一个shared_ptr对该对象进行管理时,就将该对象的引用计数减一,如果该对象的引用计数为0的时候,说明没有任何指针对其管理,才调用delete释放其所占的内存。

     1) 创建Book对象,将其分配给myBook管理,此时其使用计数为1。

     2) 将myBook的所有权共享给myBook1,查看通过这2个shared_ptr查看引用计数都为2。说明当所有权共享时,通过每个shared_ptr查看到的引用计数值是一样。

     3) 剥夺myBook的所有权,通过myBook查看到的引用计数值变为0(脱离关系,变为0),通过myBook查看到的引用计数值变为1(共享者少了1个,减1)。

     4) 当剥夺最后一个shared_ptr对其控制对象的所有权时,被管理的对象将被释放。

 

内部实现

    下面是boost::shared_ptr内部实现所涉及到的类关系图(部分类属性和成员函数省略):



     shared_ptr<T>:px代表指向具体对象的指针;pn保存引用计数相关信息。shared_ptr的核心就是shared_count,因为整个shared_ptr实现都没有出现对引用计数的具体操作,比如+1 -1等。而每一次需要用到对引用计数的操作都调用了shared_count内部封装的函数,比如:swap、==、get_deleter、use_count等。

 

     shared_count:内部包含sp_counted_base *pi_,该成员在shared_count的构造函数里初始化。

1 // 构造函数(通过被管理对象类型构造)
2 template<class Y> explicit shared_count( Y * p ): pi_( 0 )
3 {
4     ...
5     pi_ = new sp_counted_impl_p<Y>( p );
6     ...
7 }
8
9 // 拷贝构造函数
10 shared_count(shared_count const & r): pi_(r.pi_) // nothrow
11 {
12     if( pi_ != 0 ) pi_->add_ref_copy();
13 }


 

     sp_count_base:该类主要对引用计数进行管理,used_count是被引用的此时;weak_count涉及到weak_ptr的相关内容,这里就先不讨论。add_ref_copy()内部会对used_count进行自加。release()内部调用dispose(),不过dispose()需要派生类自己实现。创建派生类对象时,会先初始化sp_count_base类对象中的use_count为1。

 

     下面我们简化下场景:创建一个名为myBook的shared_ptr对象来管理一个Book对象,再通过myBook新建名为myBook1的shared_ptr对象。通过如下时序图来回顾下整个流程:



 

总结

     与裸指针相比,shared_ptr 会有一点点额外的空间代价。我还没有发现由于这些代价太大而需要另外寻找一个解决方案的情形。不要去创建你自己的引用计数智能指针类。没有比使用 shared_ptr 智能指针更好的了。和前面介绍的boost::scoped_ptr相比,boost::shared_ptr可以共享对象的所有权,因此其使用范围基本上没有什么限制,自然也可以使用在stl的容器中。另外它还是线程安全的,但boost::shared_ptr并不是绝对安全,下面几条规则能使我们更加安全的使用boost::shared_ptr:

避免对shared_ptr所管理的对象的直接内存管理操作,以免造成该对象的重释放。shared_ptr并不能对循环引用的对象内存自动管理(这点是其它各种引用计数管理内存方式的通病)。
不要构造一个临时的shared_ptr作为函数的参数(有内存泄露风险,取决于编译器厂商的实现)。

1 void f(shared_ptr<int>, int);
2 int g();
3
4 void ok()
5 {
6     shared_ptr<int> p(new int(2));
7     f(p, g());
8 }
9
10 void bad()
11 {
12     f(shared_ptr<int>(new int(2)), g());
13 }


 

http://www.cnblogs.com/helloamigo/p/3572533.html

 

[C++] Boost智能指针——boost::scoped_ptr(使用及原理分析)

简介

     boost::scoped_ptr是一个比较简单的智能指针,它能保证在离开作用域之后它所管理对象能被自动释放。下面这个例子将介绍它的使用:

1 #include <iostream>
2 #include <boost/scoped_ptr.hpp>
3
4 using namespace std;
5
6 class Book
7 {
8 public:
9     Book()
10     {
11         cout << "Creating book ..." << endl;
12     }
13
14     ~Book()
15     {
16         cout << "Destroying book ..." << endl;
17     }
18 };
19
20 int main()
21 {
22     cout << "=====Main Begin=====" << endl;
23     {
24         boost::scoped_ptr<Book> myBook(new Book());
25     }
26     cout << "===== Main End =====" << endl;
27
28     return 0;
29 }


     运行结果:



     可以看出:当myBook离开了它的作用域之后,它所管理的Book对象也随之销毁。

 

特点——不能共享控制权

     scoped_ptr不能通过其他scoped_ptr共享控制权,因为在scoped_ptr类的内部将拷贝构造函数=运算符重载定义为私有的。我们看下scoped_ptr类的定义就清楚了:

1 namespace boost
2 {
3     template<typename T> class scoped_ptr : noncopyable
4     {
5     private:
6
7         T *px;
8
9         scoped_ptr(scoped_ptr const &);
10         scoped_ptr &operator=(scoped_ptr const &);
11
12         typedef scoped_ptr<T> this_type;
13
14         void operator==( scoped_ptr const & ) const;
15         void operator!=( scoped_ptr const & ) const;
16     public:
17         explicit scoped_ptr(T *p = 0);
18         ~scoped_ptr();
19
20         explicit scoped_ptr( std::auto_ptr<T> p ): px( p.release() );
21         void reset(T *p = 0);
22
23         T &operator*() const;
24         T *operator->() const;
25         T *get() const;
26
27         void swap(scoped_ptr &b);
28     };
29
30     template<typename T>
31     void swap(scoped_ptr<T> &a, scoped_ptr<T> &b);
32 }


 

     下面这段代码中的注释部分打开会造成编译失败:

1 #include <iostream>
2 #include <boost/scoped_ptr.hpp>
3
4 using namespace std;
5
6 class Book
7 {
8 public:
9     Book()
10     {
11         cout << "Creating book ..." << endl;
12     }
13
14     ~Book()
15     {
16         cout << "Destroying book ..." << endl;
17     }
18 };
19
20 int main()
21 {
22     cout << "=====Main Begin=====" << endl;
23     {
24         boost::scoped_ptr<Book> myBook(new Book());
25         //boost::scoped_ptr<Book> myBook1(myBook);    // Error: scoped_ptr的拷贝构造函数私有
26         //boost::scoped_ptr<Book> myBook2 = myBook;   // Error: scoped_ptr的=运算符重载私有
27     }
28     cout << "===== Main End =====" << endl;
29
30     return 0;
31 }


 

    所以,scoped_ptr不能用在标准库的容器中,因为容器中的push_back操作需要调用scoped_ptr的=运算符重载函数,结果就是会导致编译失败。

1 #include <iostream>
2 #include <string>
3 #include <vector>
4 #include <boost/scoped_ptr.hpp>
5
6 using namespace std;
7
8 class Book
9 {
10 private:
11     string name_;
12
13 public:
14     Book(string name) : name_(name)
15     {
16         cout << "Creating book " << name_ << " ..." << endl;
17     }
18
19     ~Book()
20     {
21         cout << "Destroying book " << name_ << " ..." << endl;
22     }
23 };
24
25 int main()
26 {
27     cout << "=====Main Begin=====" << endl;
28     {
29         boost::scoped_ptr<Book> myBook(new Book("「1984」"));
30         vector<boost::scoped_ptr<Book>> vecScoped;
31         //vecScoped.push_back(myBook);   // Error: push_back操作内部调用了scoped_ptr的=运算符重载函数
32     }
33     cout << "===== Main End =====" << endl;
34
35     return 0;
36 }


 

编译检查=万无一失?

     虽然我们无法通过scoped_ptr的拷贝构造函数和=运算符重载函数共享控制权。那如果将一个对象交给多个scoped_ptr来管理会怎样?

1 #include <iostream>
2 #include <boost/scoped_ptr.hpp>
3
4 using namespace std;
5
6 class Book
7 {
8 public:
9     Book()
10     {
11         cout << "Creating book ..." << endl;
12     }
13
14     ~Book()
15     {
16         cout << "Destroying book ..." << endl;
17     }
18 };
19
20 int main()
21 {
22     cout << "=====Main Begin=====" << endl;
23     {
24         Book * book = new Book();
25         boost::scoped_ptr<Book> myBook(book);
26         boost::scoped_ptr<Book> myBook1(book);
27     }
28     cout << "===== Main End =====" << endl;
29
30     return 0;
31 }


      我们发现编译没报错,但是运行时出错了,如下:



     之所以会这样是因为每个scoped_ptr对象都保存了自己所管理对象指针px,scoped_ptr对象在离开自己作用域时会调用了自身的析构函数,在析构函数内部会调用delete px,当多个scoped_ptr管理同一个对象时,那么在它们离开作用域之后,势必会多次调用delete以释放它们所管理的对象,从而造成程序运行出错。

 

其他接口

     虽然scoped_ptr不能转移控制权,但是它们可以交换共享权。就以下面的代码举个例子:

1 #include <iostream>
2 #include <string>
3 #include <boost/scoped_ptr.hpp>
4
5 using namespace std;
6
7 class Book
8 {
9 private:
10     string name_;
11
12 public:
13     Book(string name) : name_(name)
14     {
15         cout << "Creating book " << name_ << " ..." << endl;
16     }
17
18     ~Book()
19     {
20         cout << "Destroying book " << name_ << " ..." << endl;
21     }
22 };
23
24 int main()
25 {
26     cout << "=====Main Begin=====" << endl;
27     {
28         boost::scoped_ptr<Book> myBook(new Book("「1984」"));
29         boost::scoped_ptr<Book> myBook1(new Book("「A Song of Ice and Fire」"));
30         myBook.swap(myBook1);
31     }
32     cout << "===== Main End =====" << endl;
33
34     return 0;
35 }


      运行结果:



     根据栈的特性,应该是后面构造的scoped_ptr对象先销毁(从而销毁了它们所管理的对象),正是因为我们对两个智能指针的控制权进行交换之后,才出现了这种相反的结果。

 

     此外,在scoped_ptr离开作用域之前也是可以显式销毁它们所管理的对象的。调用它的reset方法即可。请看下面例子:

1 #include <iostream>
2 #include <string>
3 #include <boost/scoped_ptr.hpp>
4
5 using namespace std;
6
7 class Book
8 {
9 private:
10     string name_;
11
12 public:
13     Book(string name) : name_(name)
14     {
15         cout << "Creating book " << name_ << " ..." << endl;
16     }
17
18     ~Book()
19     {
20         cout << "Destroying book " << name_ << " ..." << endl;
21     }
22 };
23
24 int main()
25 {
26     cout << "=====Main Begin=====" << endl;
27     {
28         boost::scoped_ptr<Book> myBook(new Book("「1984」"));
29         myBook.reset();
30         cout << "After reset ..." << endl;
31     }
32     cout << "===== Main End =====" << endl;
33
34     return 0;
35 }


      运行结果:



     可以看出:程序在输出“After reset ...”之前已经完成了对所管理对象的释放。

 

总结(摘自《超越C++标准库:Boost库导论》)

     使用裸指针来写异常安全和无错误的代码是很复杂的。使用智能指针来自动地把动态分配对象的生存期限制在一个明确的范围之内,是解决这种问题的一个有效的方法,并且提高了代码的可读性、可维护性和质量。scoped_ptr明确地表示被指物不能被共享和转移。当一个动态分配的对象被传送给 scoped_ptr, 它就成为了这个对象的唯一的拥有者。因为scoped_ptr几乎总是以自动变量或数据成员来分配的,因此它可以在离开作用域时正确地销毁,从而在执行流由于返回语句或异常抛出而离开作用域时,总能释放它所管理的内存。

     在以下情况时使用scoped_ptr:

在可能有异常抛出的作用域里使用指针
函数里有几条控制路径
动态分配对象的生存期应被限制于特定的作用域内
异常安全非常重要时(始终如此!)

 

 shared_ptr与scoped_ptr一样包装了new操作符在堆上分配的动态对象,但它实现的是引用计数型的智能指针,可以被自由的拷贝和赋值,当应用计数为0时,才删除动态分配的对象。shared_ptr支持标准容器,弥补了auto_ptr因为转移语义而不能把指针作为STL容器元素的缺陷。

Improving on shared_ptr

转自:http://www.codeproject.com/Tips/403919/Improving-on-shared-ptr

 

Creation of objects for using with std::shared_ptr 

In this post I will tell about an optimization I have recently discovered and successfully applied in my project. It is very simple to apply but yields very good results - in terms of both performance and memory footprint.

Let's start with the usual way
shared_ptr
s are created:

class Test {
public: Test(int dummy) {}

 } ...

std::shared_ptr<Test> pTest(new Test(1));

 

Now let's look step by step at what's going on here:

A new object of class
Test
is allocated and constructed. Note that it is allocated on the heap, and each heap allocation incurs memory overhead above
sizeof(Test)
. Allocation overheads are discussed in more detail in thisblog
post.
Then a pointer is passed to the constructor of
std::shared_ptr<Test>
which takes ownership of the newly created object. 
The mentioned constructor of
shared_ptr
basically does two things: it assigns its internal pointer to the object that it will hold, and it allocates (again on the heap!) a shared buffer that will be used
to hold reference counters. As you can see, here's another memory allocation, another performance and memory footprint hit. So the more efficient way of doing it is:

std::shared_ptr<Test> pTest = std::make_shared<Test>(1);

There are a few benefits of doing this:

There is only one memory allocation - both reference counters and the object itself are allocated in one buffer (however this is implementation-dependent, so this is the case for Visual C++ 10)
There is a constructor of
std::shared_ptr<T>
that takes an R-value reference as an argument, making this construct potentially more efficient
What are the drawbacks here you might ask? There are only a few of these:

As Visual C++ 10 (and beta of VC11) does not support variadic templates number of arguments passed to object's constructor is limited to 10 (and in VC11 to 5 with default settings)
std::make_shared
's from the boost library does not support this optimization and even in the latest version available now (1.49.0) allocates two separate buffers - one for the object and one for reference
counters
So anyway, if you have
boost::shared_ptr
s and you are usingboost starting from 1.36.0 in your project (that's the version that
make_shared

was copied from tr1 to boost library), it might still make sense to migrate to
make_shared
even if you won't get the benefits right away.

Is it that simple?

No, there is one small pitfall I hit when I migrated my project to using
std::make_shared
. When the object is being created by some factory class, the constructor is declared
private
and the factory class is declared as a
friend
. The problem lies in the fact that when the factory creates a new object
with
make_shared
the constructor is called from some unknown implementation-specific class, and this call fails to compile as we can't declare it a
friend
of our class. The solution is not very elegant (at least for my taste), but it
works, and helps when this optimization is applied to make a real difference in performance. Here is a sample code:

class Obj;

 typedef std::shared_ptr ObjPtr;

class Obj {

...

 private:

struct HideMe {}

 friend class ObjFactory;

 public:

Obj(HideMe, int param) { ... }

 }

class ObjFactory

{
public:

static ObjPtr CreateObj()

 {

 return std::make_shared(Obj::HideMe(), param) ;

 }

}

 

So the actual constructor that is being called by
ObjFactory
is
public
, but no other object could invoke it due to the hidden structure
Obj::HideMe
.

Happy optimizing!

 

 

boost 桥接设计模式例子:摘自:boost程序库完全开发指南

#include <boost/smart_ptr.hpp>
using namespace boost;

#include <iostream>
using namespace std;

class sample
{
private:
class impl;shared_ptr<impl>p;public:
sample();
void print();
};

class sample::impl{public:
void print()
{
cout<<"impl print"<<endl;
}
};

sample::sample():p(new impl)
{

}

void sample::print()
{
p->print();
}

int _tmain(int argc, _TCHAR* argv[])
{
//
sample s;
s.print();
}


 3)Smart Pointers to boost your code

http://www.codeproject.com/Articles/8394/Smart-Pointers-to-boost-your-code

 

What are Smart Pointers?

The name should already give it away:

A Smart Pointer is a C++ object that acts like a pointer, but additionally deletes the object when it is no longer needed.

"No longer needed" is hard to define, since resource management in C++ is very complex. Different smart pointer implementations cover the most common scenarios. Of course, different tasks than just deleting the object can be implemented too, but these applications
are beyond the scope of this tutorial.

Many libraries provide smart pointer implementations with different advantages and drawbacks. The samples here use theBOOST library, a high quality open
source template library, with many submissions considered for inclusion in the next C++ standard.

Boost provides the following smart pointer implementations:

shared_ptr<T>
pointer to
T"
using a reference count to determine when the object is no longer needed.
shared_ptr

is the generic, most versatile smart pointer offered by boost.
scoped_ptr<T>
a pointer automatically deleted when it goes out of scope. No assignment possible, but no performance penalties compared to "raw" pointers
intrusive_ptr<T>
another reference counting pointer. It provides better performance than
shared_ptr
, but requires the type
T
to provide its own reference counting mechanism.
weak_ptr<T>
a weak pointer, working in conjunction with
shared_ptr
to avoid circular references
shared_array<T>
like
shared_ptr
, but access syntax is for an Array of
T
scoped_array<T>
like
scoped_ptr
, but access syntax is for an Array of
T
Let's start with the simplest one:

The first: boost::scoped_ptr<T>

scoped_ptr
is the simplest smart pointer provided by boost. It guarantees automatic deletion when the pointer goes out of scope.

A note on the samples:

The samples use a helper class,
CSample
, that prints diagnostic messages when it it constructed, assigned, or destroyed. Still it might be interesting to step through with
the debugger. The sample includes the required parts of boost, so no additional downloads are necessary - but please read the boost installations notes, below.

The following sample uses a
scoped_ptr
for automatic destruction:

Using normal pointersUsing
scoped_ptr


Collapse | Copy Code


void Sample1_Plain()
{
CSample * pSample(new CSample);

if (!pSample->Query() )
// just some function...
{
delete pSample;
return;
}

pSample->Use();
delete pSample;

}


Collapse |Copy
Code
#include "boost/smart_ptr.h"

void Sample1_ScopedPtr()
{
boost::scoped_ptr<CSample>
samplePtr(new CSample);

if (!samplePtr->Query() )
// just some function...
return;

samplePtr->Use();

}

Using "normal" pointers, we must remember to delete it at every place we exit the function. This is especially tiresome (and easily forgotten) when using exceptions. The second example uses a
scoped_ptr

for the same task. It automatically deletes the pointer when the function returns 8 even in the case of an exception thrown, which isn't even covered in the "raw pointer" sample!)

The advantage is obvious: in a more complex function, it's easy to forget to delete an object.
scoped_ptr
does it for you. Also, when dereferencing a
NULL

pointer, you get an assertion in debug mode.

use forautomatic deletion of local objects or class members1, Delayed Instantiation, implementing PIMPL and RAII (see below)
not good forelement in an STL container, multiple pointers to the same object
performance:
scoped_ptr
adds little (if any) overhead to a "plain" pointer, it performs
For this purpose, using
scoped_ptr
is more expressive than the (easy to misuse and more complex)
std::auto_ptr
:
using
scoped_ptr
, you indicate that ownership transfer is not intended or allowed.

Reference counting pointers

Reference counting pointers track how many pointers are referring to an object, and when the last pointer to an object is destroyed, it deletes the object itself, too.

The "normal" reference counted pointer provided by boost is
shared_ptr
(the name indicates that multiple pointers can share the same object). Let's look at a few examples:


Collapse |Copy
Code
void Sample2_Shared()
{
// (A) create a new CSample instance with one reference
boost::shared_ptr<CSample> mySample(new CSample);
printf("The Sample now has %i references\n", mySample.use_count()); // should be 1

// (B) assign a second pointer to it:
boost::shared_ptr<CSample> mySample2 = mySample; // should be 2 refs by now
printf("The Sample now has %i references\n", mySample.use_count());

// (C) set the first pointer to NULL
mySample.reset();
printf("The Sample now has %i references\n", mySample2.use_count());  // 1

// the object allocated in (1) is deleted automatically
// when mySample2 goes out of scope
}

Line (A) creates a new
CSample
instance on the heap, and assigns the pointer to a
shared_ptr
,
mySample
.
Things look like this:



Then, we assign it to a second pointer
mySample2
. Now, two pointers access the same data:



We reset the first pointer (equivalent to
p=NULL
for a raw pointer). The
CSample
instance
is still there, since
mySample2
holds a reference to it:



Only when the last reference,
mySample2
, goes out of scope, the
CSample
is destroyed
with it:



Of course, this is not limited to a single
CSample
instance, or two pointers, or a single function. Here are some use cases for a
shared_ptr
.

use in containers
using the pointer-to-implementation idiom (PIMPL)
Resource-Acquisition-Is-Initialization (RAII) idiom
Separating Interface from Implementation
Note: If you never heard of PIMPL (a.k.a. handle/body) or RAII, grab a good C++ book - they are important concepts every C++ programmer should know. Smart pointers are just one way to implement them conveniently
in certain cases - discussing them here would break the limits of this article.

Important Features

The
boost::shared_ptr
implementation has some important features that make it stand out from other implementations:

shared_ptr<T>
works with an incomplete type:
When declaring or using a
shared_ptr<T>
,
T
may be an "incomplete type". E.g., you do
only a forward declaration using
class T;
. But do not yet define how
T

really looks like. Only where you dereference the pointer, the compiler needs to know "everything".

shared_ptr<T>
works with any type:
There are virtually no requirements towards
T
(such as deriving from a base class).

shared_ptr<T>
supports a custom deleter
So you can store objects that need a different cleanup than
delete p
. For more information, see
the boost documentation.

Implicit conversion:
If a type
U *
can be implicitly converted to
T *
(e.g., because
T

is base class of
U
), a
shared_ptr<U>
can also be converted to
shared_ptr<T>

implicitly.

shared_ptr
is thread safe
(This is a design choice rather than an advantage, however, it is a necessity in multithreaded programs, and the overhead is low.)

Works on many platforms, proven and peer-reviewed, the usual things.

Example: Using shared_ptr in containers

Many container classes, including the STL containers, require copy operations (e.g., when inserting an existing element into a list, vector, or container). However, when this copy operations are expensive (or are even unavailable), the typical solution is
to use a container of pointers:


Collapse |Copy
Code
std::vector<CMyLargeClass *> vec;
vec.push_back( new CMyLargeClass("bigString") );

However, this throws the task of memory management back to the caller. We can, however, use a
shared_ptr
:


Collapse |Copy
Code
typedef boost::shared_ptr<CMyLargeClass>  CMyLargeClassPtr;
std::vector<CMyLargeClassPtr> vec;
vec.push_back( CMyLargeClassPtr(new CMyLargeClass("bigString")) );

Very similar, but now, the elements get destroyed automatically when the vector is destroyed - unless, of course, there's another smart pointer still holding a reference. Let's have a look at sample 3:


Collapse |Copy
Code
void Sample3_Container()
{
typedef boost::shared_ptr<CSample> CSamplePtr;

// (A) create a container of CSample pointers:
std::vector<CSamplePtr> vec;

// (B) add three elements
vec.push_back(CSamplePtr(new CSample));
vec.push_back(CSamplePtr(new CSample));
vec.push_back(CSamplePtr(new CSample));

// (C) "keep" a pointer to the second:
CSamplePtr anElement = vec[1];

// (D) destroy the vector:
vec.clear();

// (E) the second element still exists
anElement->Use();
printf("done. cleanup is automatic\n");

// (F) anElement goes out of scope, deleting the last CSample instance
}

What you absolutely must know to use boost smart pointers correctly

A few things can go wrong with smart pointers (most prominent is an invalid reference count, which deletes the object too early, or not at all). The boost implementation promotes safety, making all "potentially dangerous" operations explicit. So, with a
few rules to remember, you are safe.

There are a few rules you should (or must) follow, though:

Rule 1: Assign and keep - Assign a newly constructed instance to a smart pointer immediately, and then keep it there. The smart pointer(s) now own the object, you must not delete it manually, nor can you take it away again. This helps to
not accidentally delete an object that is still referenced by a smart pointer, or end up with an invalid reference count.

Rule 2: a
_ptr<T>
is not a
T *
- more correctly,
there are no implicit conversions between a
T *
and a smart pointer to type
T
.

This means:

When creating a smart pointer, you explicitly have to write
..._ptr<T> myPtr(new T)

You cannot assign a
T *
to a smart pointer
You cannot even write
ptr=NULL
. Use
ptr.reset()
for that.
To retrieve the raw pointer, use
ptr.get()
. Of course, you must not delete this pointer, or use it after the smart pointer it comes from is destroyed, reset or reassigned.
Use
get()
only when you have to pass the pointer to a function that expects a raw pointer.
You cannot pass a
T *
to a function that expects a
_ptr<T>
directly. You have to construct
a smart pointer explicitly, which also makes it clear that you transfer ownership of the raw pointer to the smart pointer. (See also Rule 3.)
There is no generic way to find the smart pointer that "holds" a given raw pointer. However, theboost: smart pointer programming techniques
illustrate solutions for many common cases.
Rule 2: No circular references - If you have two objects referencing each other through a reference counting pointer, they are never deleted. boost provides
weak_ptr

to break such cycles (see below).

Rule 3: no temporary shared_ptr - Do not construct temporary
shared_ptr
to pass them to functions, always use a named (local) variable. (This makes your code safe in case of exceptions. See theboost:shared_ptr best practices for a detailed explanation.)

Cyclic References

Reference counting is a convenient resource management mechanism, it has one fundamental drawback though: cyclic references are not freed automatically, and are hard to detect by the computer. The simplest example is this:


Collapse |Copy
Code
struct CDad;
struct CChild;

typedef boost::shared_ptr<CDad>   CDadPtr;
typedef boost::shared_ptr<CChild> CChildPtr;

struct CDad : public CSample
{
CChildPtr myBoy;
};

struct CChild : public CSample
{
CDadPtr myDad;
};

// a "thing" that holds a smart pointer to another "thing":

CDadPtr   parent(new CDadPtr);
CChildPtr child(new CChildPtr);

// deliberately create a circular reference:
parent->myBoy = child;
child->myDad = dad;

// resetting one ptr...
child.reset();

parent
still references the
CDad
object, which itself references the
CChild
.
The whole thing looks like this:



If we now call
dad.reset()
, we lose all "contact" with the two objects. But this leaves both with exactly one reference, and the shared pointers see no reason to delete
either of them! We have no access to them anymore, but they mutually keep themselves "alive". This is a memory leak at best; in the worst case, the objects hold even more critical resources that are not released correctly.

The problem is not solvable with a "better" shared pointer implementation (or at least, only with unacceptable overhead and restrictions). So you have to break that cycle. There are two ways:

Manually break the cycle before you release your last reference to it
When the lifetime of
Dad
is known to exceed the lifetime of
Child
, the child can use
a normal (raw) pointer to
Dad
.
Use a
boost::weak_ptr
to break the cycle.
Solutions (1) and (2) are no perfect solutions, but they work with smart pointer libraries that do not offer a
weak_ptr
like boost does. But let's look at
weak_ptr

in detail:

Using weak_ptr to break cycles

Strong vs. Weak References:

A strong reference keeps the referenced object alive (i.e., as long as there is at least one strong reference to the object, it is not deleted).
boost::shared_ptr
acts as
a strong reference. In contrast, a weak reference does not keep the object alive, it merely references it as long as it lives.

Note that a raw C++ pointer in this sense is a weak reference. However, if you have just the pointer, you have no ability to detect whether the object still lives.

boost::weak_ptr<T>
is a smart pointer acting as weak reference. When you need it, you can request a strong (shared) pointer from it. (This can be
NULL

if the object was already deleted.) Of course, the strong pointer should be released immediately after use. In the above sample, we can decide to make one pointer weak:


Collapse |Copy
Code
struct CBetterChild : public CSample
{
weak_ptr<CDad> myDad;

void BringBeer()
{shared_ptr<CDad> strongDad = myDad.lock(); // request a strong pointer
if (strongDad) // is the object still alive?
strongDad->SetBeer();
// strongDad is released when it goes out of scope.
// the object retains the weak pointer
}
};

See the Sample 5 for more.

intrusive_ptr - lightweight shared pointer

shared_ptr
offers quite some services beyond a "normal" pointer. This has a little price: the size of a shared pointer is larger than a normal pointer, and for each object
held in a shared pointer, there is a tracking object holding the reference count and the deleter. In most cases, this is negligible.

intrusive_ptr
provides an interesting tradeoff: it provides the "lightest possible" reference counting pointer, if the object implements the reference count itself. This
isn't so bad after all, when designing your own classes to work with smart pointers; it is easy to embed the reference count in the class itself, to get less memory footprint and better performance.

To use a type
T
with
intrusive_ptr
, you need to define two functions:
intrusive_ptr_add_ref

and
intrusive_ptr_release
. The following sample shows how to do that for a custom class:


Collapse |Copy
Code
#include "boost/intrusive_ptr.hpp"

// forward declarations
class CRefCounted;

namespace boost
{
void intrusive_ptr_add_ref(CRefCounted * p);
void intrusive_ptr_release(CRefCounted * p);
};

// My Class
class CRefCounted
{
private:
long references;
friend void ::boost::intrusive_ptr_add_ref(CRefCounted * p);
friend void ::boost::intrusive_ptr_release(CRefCounted * p);
public:
CRefCounted() : references(0) {} // initialize references to 0
};

// class specific addref/release implementation
// the two function overloads must be in the boost namespace on most compilers:
namespace boost
{
inline void intrusive_ptr_add_ref(CRefCounted * p)
{
// increment reference count of object *p
++(p->references);
}

inline void intrusive_ptr_release(CRefCounted * p)
{
// decrement reference count, and delete object when reference count reaches 0
if (--(p->references) == 0)
delete p;
}
} // namespace boost

This is the most simplistic (and not thread safe) implementation. However, this is such a common pattern, that it makes sense to provide a common base class for this task. Maybe another article


scoped_array and shared_array

They are almost identical to
scoped_ptr
and
shared_ptr
- only they act like pointers
to arrays, i.e., like pointers that were allocated using
operatornew[]
.
They provide an overloaded
operator[]
. Note that neither of them knows the length initially allocated.

Installing Boost

Download the current boost version from
boost.org, and unzip it to a folder of your choice. The unzipped sources use the following structure (using my folders):

boost\the actual boost sources / headers
doc\the documentation of the current version, in HTML format
libs\libraries (not needed for
....
some more odd bits and ends ("more\" has some interesting stuff)
I add this folder to the common includes of my IDE:

in VC6, this is Tools/Options, Directories tab, "Show Directories for... Include files",
in VC7, this is Tools/Options, then Projects/VC++ directories, "Show Directories for... Include files".
Since the actual headers are in the boost\ subfolder, my sources has
#include "boost/smart_ptr.hpp"
. So everybody reading the source code knows immediately you are using boost smart pointers, not just any ones.

Note about the sample project

The sample project contains a sub folder boost\ with a selection of boost headers required. This is merely so you can download and compile the sample. You should really download the complete and most current sources (now!).

VC6: the min/max tragedy

There is a "little" problem with VC6 that makes using boost (and other libraries) a bit problematic out of the box.

The Windows header files define macros for
min
and
max
, and consequently, these respective
functions are missing from the (original) STL implementation. Some Windows libraries such as MFC rely on
min
/
max

being present. Boost, however, expects
min
and
max
in the
std::

namespace. To make things worse, there is no feasible
min
/
max
template that accepts different
(implicitly convertible) argument types, but some libraries rely on that.

boost tries to fix that as good as they can, but sometimes you will run into problems. If this happens, here's what I do: put the following code before the first
include
:


Collapse |Copy
Code
#define _NOMINMAX            // disable windows.h defining min and max as macros
#include "boost/config.hpp"  // include boosts compiler-specific "fixes"
using std::min;              // makle them globally available
using std::max;

This solution (as any other) isn't without problems either, but it worked in all cases I needed it, and it's just one place to put it.

Resources

Not enough information? More Questions?

Boost home page
Download boost
smart pointer overview
boost
users mailing list
Questions about boost? That's the place to go.

Articles on Code Project:

An Introduction to boost by Andrew Walker
Designing Robust Objects with boost by Jim D'Agostino, a very interesting article: it seems to cover too much topics
at once, but it excels at showing how different tools, mechanisms, paradigms, libraries, etc. work together.
Please note: While I am happy about (almost) any feedback, please do not ask boost-specific questions here. Simply put, boost experts are unlikely to find your question here (and I'm just a boost noob). Of course, if you
have questions, complaints, or recommendations regarding the article or the sample project, you are welcome.

History

Sept 05, 2004: Initial version
Sept 27, 2004: Published to CodeProject
Sept 29, 2004: Minor Fixes

License

This article, along with any associated source code and files, is licensed underThe Code Project Open License (CPOL)

 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: