boost智能指针怎样会出现指针 内存泄漏漏

1.STL的指针auto_ptr当拷贝构造或者赋值时会发生控制权的转移,所以不能被当做元素存储到数组或者标准库的容器中去,当然也不能用指向数组的指针去初始化一个auto_pt对象。下面介绍几个此类的几个关键方法。
ap.reset(p);& 如果p与ap的值不相同,则删除ap指向的对象并且将ap绑定到p。
ap.release(); 返回ap所保存的指针并且使ap成为未绑定的。
ap.get(); 返回ap所保存的指针。
2.boost::scoped_ptr/boost::scoped::array。
这两个只能指针不会存在所有权转移的问题,因为他们的拷贝构造函数和=操作符被声明为私有成员。。。重载了*和-&操作符;还有一个reset()函数可以手动销毁指向的对象。
3.boost::shared_ptr/boost::shared_array。
该智能指针也重载了*和-&操作符;
reset()也是用于显式手动销毁对象的函数;
use_count()用来检查当前指向所管理对象的shared_ptr的个数。
由于采用和引用计数机制来实现共享语意,所以可以存储到STL的容器中去,但是不可避免的出现了循环计数的问题。此问题用boost::weak_ptr来解决。boost::weak_ptr中存储的是指向一个已经由boost::shared_ptr所管理对象的弱引用,其中没有重载operator-&。因此当需要访问该对象时,需要将boost::weak_ptr转换为boost::shared_ptr来进行。这种转换可以通过构造一个新的boost::shared_ptr或者调用boost::weak_ptr的lock()方法进行。如果此刻该对象已经不存在了,则这两种方法都将失败。lock()会返回空,而boost::shared_ptr的构造函数将会抛出boost::bad_weak_ptr异常。请看一个例子:
#include &iostream&#include &boost/smart_ptr.hpp&
void testptr(){&boost::shared_ptr&simple& psimple(new simple);&boost::weak_ptr&simple& psimple2(psimple);&&// psimple.reset();&&if (psimple2.lock())&{&&psimple2.lock()-&foo();&}&else&{&&cout&&"simple is gone"&&&}&&try&{&&boost::shared_ptr&simple& psimple3(psimple2);&&psimple3-&foo();&}&catch(boost::bad_weak_ptr)&{&&cout&&"simple has gone, can not construct shared_ptr"&&&}&&cout&&"exit sharedPtr()"&&}
int main(){&testptr();&cout&&"exit main()"&&&return 0;}
当把psimple.reset()注释掉的时候,没有用该函数销毁对象,这个boost::weak_ptr是可以正常访问的该对象的。
若没注释掉,即会调用该函数来销毁对象,然后再使用boost::weak_ptr就会发生错误!!!
阅读(...) 评论()下次自动登录
现在的位置:
& 综合 & 正文
Boost 内存管理—-智能指针
shared_ptr 指针
boost::shared_ptr是可以共享所有权的指针。如果有多个shared_ptr共同管理同一个对象时,只有这些shared_ptr全部与该对象脱离关系之后,被管理的对象才会被释放。通过下面这个例子先了解下shared_ptr的基本用法:
1 #include &iostream&
2 #include &string&
3 #include &boost/shared_ptr.hpp&
5 using namespace
7 class Book
9 private:
<span style="color:#
string name_;
<span style="color:#
<span style="color:# public:
<span style="color:#
Book(string name) : name_(name)
<span style="color:#
<span style="color:#
cout && "Creating book " && name_ && " ..." &&
<span style="color:#
<span style="color:#
<span style="color:#
<span style="color:#
<span style="color:#
cout && "Destroying book " && name_ && " ..." &&
<span style="color:#
<span style="color:# };
<span style="color:#
<span style="color:# int main()
<span style="color:# {
<span style="color:#
cout && "=====Main Begin=====" &&
<span style="color:#
<span style="color:#
boost::shared_ptr&Book& myBook(new Book("「1984」"));
<span style="color:#
cout && "[From myBook] The ref count of book is " && myBook.use_count() && ".\n" &&
<span style="color:#
<span style="color:#
boost::shared_ptr&Book& myBook1(myBook);
<span style="color:#
cout && "[From myBook] The ref count of book is " && myBook.use_count() && "." &&
<span style="color:#
cout && "[From myBook1] The ref count of book is " && myBook1.use_count() && ".\n" &&
<span style="color:#
<span style="color:#
cout && "Reset for 1th time. Begin..." &&
<span style="color:#
myBook.reset();
<span style="color:#
cout && "[From myBook] The ref count of book is " && myBook.use_count() && "." &&
<span style="color:#
cout && "[From myBook1] The ref count of book is " && myBook1.use_count() && "." &&
<span style="color:#
cout && "Reset for 1th time. End ...\n" &&
<span style="color:#
<span style="color:#
cout && "Reset for 2th time. Begin ..." &&
<span style="color:#
myBook1.reset();
<span style="color:#
cout && "Reset for 2th time. End ..." &&
<span style="color:#
<span style="color:#
cout && "===== Main End =====" &&
<span style="color:#
<span style="color:#
return <span style="color:#;
<span style="color:# }
运行结果:
运行过程分析:
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_( <span style="color:# )
pi_ = new sp_counted_impl_p&Y&( p );
9 // 拷贝构造函数
<span style="color:# shared_count(shared_count const & r): pi_(r.pi_) // nothrow
<span style="color:# {
<span style="color:#
if( pi_ != <span style="color:# ) pi_-&add_ref_copy();
<span style="color:# }
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();
4 void ok()
shared_ptr&int& p(new int(<span style="color:#));
f(p, g());
<span style="color:# void bad()
<span style="color:# {
<span style="color:#
f(shared_ptr&int&(new int(<span style="color:#)), g());
<span style="color:# }
boost::scoped_ptr是一个比较简单的智能指针,它能保证在离开作用域之后它所管理对象能被自动释放。下面这个例子将介绍它的使用:
1 #include &iostream&
2 #include &boost/scoped_ptr.hpp&
4 using namespace
6 class Book
<span style="color:#
<span style="color:#
cout && "Creating book ..." &&
<span style="color:#
<span style="color:#
<span style="color:#
<span style="color:#
<span style="color:#
cout && "Destroying book ..." &&
<span style="color:#
<span style="color:# };
<span style="color:#
<span style="color:# int main()
<span style="color:# {
<span style="color:#
cout && "=====Main Begin=====" &&
<span style="color:#
<span style="color:#
boost::scoped_ptr&Book& myBook(new Book());
<span style="color:#
<span style="color:#
cout && "===== Main End =====" &&
<span style="color:#
<span style="color:#
return <span style="color:#;
<span style="color:# }
运行结果:
可以看出:当myBook离开了它的作用域之后,它所管理的Book对象也随之销毁。
特点——不能共享控制权
scoped_ptr不能通过其他scoped_ptr共享控制权,因为在scoped_ptr类的内部将拷贝构造函数和=运算符重载定义为私有的。我们看下scoped_ptr类的定义就清楚了:
1 namespace boost
template&typename T& class scoped_ptr : noncopyable
scoped_ptr(scoped_ptr const &);
<span style="color:#
scoped_ptr &operator=(scoped_ptr const &);
<span style="color:#
<span style="color:#
typedef scoped_ptr&T& this_
<span style="color:#
<span style="color:#
void operator==( scoped_ptr const & ) const;
<span style="color:#
void operator!=( scoped_ptr const & ) const;
<span style="color:#
<span style="color:#
explicit scoped_ptr(T *p = <span style="color:#);
<span style="color:#
~scoped_ptr();
<span style="color:#
<span style="color:#
explicit scoped_ptr( std::auto_ptr&T& p ): px( p.release() );
<span style="color:#
void reset(T *p = <span style="color:#);
<span style="color:#
<span style="color:#
T &operator*() const;
<span style="color:#
T *operator-&() const;
<span style="color:#
T *get() const;
<span style="color:#
<span style="color:#
void swap(scoped_ptr &b);
<span style="color:#
<span style="color:#
<span style="color:#
template&typename T&
<span style="color:#
void swap(scoped_ptr&T& &a, scoped_ptr&T& &b);
<span style="color:# }
下面这段代码中的注释部分打开会造成编译失败:
1 #include &iostream&
2 #include &boost/scoped_ptr.hpp&
4 using namespace
6 class Book
<span style="color:#
<span style="color:#
cout && "Creating book ..." &&
<span style="color:#
<span style="color:#
<span style="color:#
<span style="color:#
<span style="color:#
cout && "Destroying book ..." &&
<span style="color:#
<span style="color:# };
<span style="color:#
<span style="color:# int main()
<span style="color:# {
<span style="color:#
cout && "=====Main Begin=====" &&
<span style="color:#
<span style="color:#
boost::scoped_ptr&Book& myBook(new Book());
<span style="color:#
//boost::scoped_ptr&Book& myBook1(myBook);
// Error: scoped_ptr的拷贝构造函数私有
<span style="color:#
//boost::scoped_ptr&Book& myBook2 = myB
// Error: scoped_ptr的=运算符重载私有
<span style="color:#
<span style="color:#
cout && "===== Main End =====" &&
<span style="color:#
<span style="color:#
return <span style="color:#;
<span style="color:# }
所以,scoped_ptr不能用在标准库的容器中,因为容器中的push_back操作需要调用scoped_ptr的=运算符重载函数,结果就是会导致编译失败。
1 #include &iostream&
2 #include &string&
3 #include &vector&
4 #include &boost/scoped_ptr.hpp&
6 using namespace
8 class Book
<span style="color:# private:
<span style="color:#
string name_;
<span style="color:#
<span style="color:# public:
<span style="color:#
Book(string name) : name_(name)
<span style="color:#
<span style="color:#
cout && "Creating book " && name_ && " ..." &&
<span style="color:#
<span style="color:#
<span style="color:#
<span style="color:#
<span style="color:#
cout && "Destroying book " && name_ && " ..." &&
<span style="color:#
<span style="color:# };
<span style="color:#
<span style="color:# int main()
<span style="color:# {
<span style="color:#
cout && "=====Main Begin=====" &&
<span style="color:#
<span style="color:#
boost::scoped_ptr&Book& myBook(new Book("「1984」"));
<span style="color:#
vector&boost::scoped_ptr&Book&& vecS
<span style="color:#
//vecScoped.push_back(myBook);
// Error: push_back操作内部调用了scoped_ptr的=运算符重载函数
<span style="color:#
<span style="color:#
cout && "===== Main End =====" &&
<span style="color:#
<span style="color:#
return <span style="color:#;
<span style="color:# }
编译检查=万无一失?
虽然我们无法通过scoped_ptr的拷贝构造函数和=运算符重载函数共享控制权。那如果将一个对象交给多个scoped_ptr来管理会怎样?
1 #include &iostream&
2 #include &boost/scoped_ptr.hpp&
4 using namespace
6 class Book
<span style="color:#
<span style="color:#
cout && "Creating book ..." &&
<span style="color:#
<span style="color:#
<span style="color:#
<span style="color:#
<span style="color:#
cout && "Destroying book ..." &&
<span style="color:#
<span style="color:# };
<span style="color:#
<span style="color:# int main()
<span style="color:# {
<span style="color:#
cout && "=====Main Begin=====" &&
<span style="color:#
<span style="color:#
Book * book = new Book();
<span style="color:#
boost::scoped_ptr&Book& myBook(book);
<span style="color:#
boost::scoped_ptr&Book& myBook1(book);
<span style="color:#
<span style="color:#
cout && "===== Main End =====" &&
<span style="color:#
<span style="color:#
return <span style="color:#;
<span style="color:# }
我们发现编译没报错,但是运行时出错了,如下:
之所以会这样是因为每个scoped_ptr对象都保存了自己所管理对象指针px,scoped_ptr对象在离开自己作用域时会调用了自身的析构函数,在析构函数内部会调用delete px,当多个scoped_ptr管理同一个对象时,那么在它们离开作用域之后,势必会多次调用delete以释放它们所管理的对象,从而造成程序运行出错。
虽然scoped_ptr不能转移控制权,但是它们可以交换共享权。就以下面的代码举个例子:
1 #include &iostream&
2 #include &string&
3 #include &boost/scoped_ptr.hpp&
5 using namespace
7 class Book
9 private:
<span style="color:#
string name_;
<span style="color:#
<span style="color:# public:
<span style="color:#
Book(string name) : name_(name)
<span style="color:#
<span style="color:#
cout && "Creating book " && name_ && " ..." &&
<span style="color:#
<span style="color:#
<span style="color:#
<span style="color:#
<span style="color:#
cout && "Destroying book " && name_ && " ..." &&
<span style="color:#
<span style="color:# };
<span style="color:#
<span style="color:# int main()
<span style="color:# {
<span style="color:#
cout && "=====Main Begin=====" &&
<span style="color:#
<span style="color:#
boost::scoped_ptr&Book& myBook(new Book("「1984」"));
<span style="color:#
boost::scoped_ptr&Book& myBook1(new Book("「A Song of Ice and Fire」"));
<span style="color:#
myBook.swap(myBook1);
<span style="color:#
<span style="color:#
cout && "===== Main End =====" &&
<span style="color:#
<span style="color:#
return <span style="color:#;
<span style="color:# }
运行结果:
根据栈的特性,应该是后面构造的scoped_ptr对象先销毁(从而销毁了它们所管理的对象),正是因为我们对两个智能指针的控制权进行交换之后,才出现了这种相反的结果。
此外,在scoped_ptr离开作用域之前也是可以显式销毁它们所管理的对象的。调用它的reset方法即可。请看下面例子:
1 #include &iostream&
2 #include &string&
3 #include &boost/scoped_ptr.hpp&
5 using namespace
7 class Book
9 private:
<span style="color:#
string name_;
<span style="color:#
<span style="color:# public:
<span style="color:#
Book(string name) : name_(name)
<span style="color:#
<span style="color:#
cout && "Creating book " && name_ && " ..." &&
<span style="color:#
<span style="color:#
<span style="color:#
<span style="color:#
<span style="color:#
cout && "Destroying book " && name_ && " ..." &&
<span style="color:#
<span style="color:# };
<span style="color:#
<span style="color:# int main()
<span style="color:# {
<span style="color:#
cout && "=====Main Begin=====" &&
<span style="color:#
<span style="color:#
boost::scoped_ptr&Book& myBook(new Book("「1984」"));
<span style="color:#
myBook.reset();
<span style="color:#
cout && "After reset ..." &&
<span style="color:#
<span style="color:#
cout && "===== Main End =====" &&
<span style="color:#
<span style="color:#
return <span style="color:#;
<span style="color:# }
运行结果:
可以看出:程序在输出“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
Creation of objects for using with std::
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 s are created:
class Test {
public: Test(int dummy) {}
std::&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 abovesizeof(Test). Allocation overheads are discussed in more detail in this.
Then a pointer is passed to the constructor of std::&Test& which takes ownership of the newly created object.
The mentioned constructor of
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::&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::&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
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
So anyway, if you have ::s and you are using starting from 1.36.0 in your project (that's the version thatmake_shared
was copied from tr1 to
library), it might still make sense to migrate tomake_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 declaredprivate and the factory class is declared as afriend. The problem lies in the fact that when the factory creates a new object
withmake_shared the constructor is called from some unknown implementation-specific class, and this call fails to compile as we can't declare it afriend 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:
typedef std:: ObjP
class Obj {
struct HideMe {}
friend class ObjF
Obj(HideMe, int param) { ... }
class ObjFactory
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&
#include &iostream&
class sample
shared_ptr&impl&p;
void print();
class sample::impl{
void print()
cout&&"impl print"&&
sample::sample():p(new impl)
void sample::print()
p-&print();
int _tmain(int argc, _TCHAR* argv[])
s.print();
3)Smart Pointers to boost your code
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 the, 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 typeT 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 ofT
scoped_array&T&
like scoped_ptr, but access syntax is for an Array ofT
Let's start with the simplest one:
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 pointers
Using scoped_ptr
Collapse |
void Sample1_Plain()
CSample * pSample(new CSample);
if (!pSample-&Query() )
pSample-&Use();
Collapse |
#include "boost/smart_ptr.h"
void Sample1_ScopedPtr()
boost::scoped_ptr&CSample&
samplePtr(new CSample);
if (!samplePtr-&Query() )
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 ascoped_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 aNULL
pointer, you get an assertion in debug mode.
automatic deletion of local objects or class members1, Delayed Instantiation, implementing PIMPL and RAII (see below)
not good for
element 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:
usingscoped_ptr, you indicate that ownership transfer is not intended or allowed.
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 |
void Sample2_Shared()
boost::shared_ptr&CSample& mySample(new CSample);
printf("The Sample now has %i references\n", mySample.use_count());
boost::shared_ptr&CSample& mySample2 = myS
printf("The Sample now has %i references\n", mySample.use_count());
mySample.reset();
printf("The Sample now has %i references\n", mySample2.use_count());
Line (A) creates a new CSample instance on the heap, and assigns the pointer to ashared_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). TheCSample instance
is still there, sincemySample2 holds a reference to it:
Only when the last reference, mySample2, goes out of scope, theCSample is destroyed
Of course, this is not limited to a single CSample instance, or two pointers, or a single function. Here are some use cases for ashared_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.
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 usingclass T;. But do not yet define howT
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 toT * (e.g., becauseT
is base class ofU), ashared_ptr&U& can also be converted toshared_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.
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 |
std::vector&CMyLargeClass *&
vec.push_back( new CMyLargeClass("bigString") );
However, this throws the task of memory management back to the caller. We can, however, use ashared_ptr:
Collapse |
typedef boost::shared_ptr&CMyLargeClass&
CMyLargeClassP
std::vector&CMyLargeClassPtr&
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 |
void Sample3_Container()
typedef boost::shared_ptr&CSample& CSampleP
std::vector&CSamplePtr&
vec.push_back(CSamplePtr(new CSample));
vec.push_back(CSamplePtr(new CSample));
vec.push_back(CSamplePtr(new CSample));
CSamplePtr anElement = vec[<span style="color:#];
vec.clear();
anElement-&Use();
printf("done. cleanup is automatic\n");
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 aT * - more correctly,
there are no implicit conversions between aT * and a smart pointer to typeT.
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. Useptr.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, the
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 providesweak_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 the for a detailed explanation.)
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 |
typedef boost::shared_ptr&CDad&
typedef boost::shared_ptr&CChild& CChildP
struct CDad : public CSample
CChildPtr myB
struct CChild : public CSample
CDadPtr myD
parent(new CDadPtr);
CChildPtr child(new CChildPtr);
parent-&myBoy =
child-&myDad =
child.reset();
parent still references theCDad object, which itself references theCChild.
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 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 ofChild, the child can use
a normal (raw) pointer toDad.
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 aweak_ptr like boost does. But let's look atweak_ptr
in detail:
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 beNULL
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 |
struct CBetterChild : public CSample
weak_ptr&CDad& myD
void BringBeer()
shared_ptr&CDad& strongDad = myDad.lock();
if (strongDad)
strongDad-&SetBeer();
See the Sample 5 for more.
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 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 withintrusive_ptr, you need to define two functions:intrusive_ptr_add_ref
andintrusive_ptr_release. The following sample shows how to do that for a custom class:
Collapse |
#include "boost/intrusive_ptr.hpp"
class CRefC
namespace boost
void intrusive_ptr_add_ref(CRefCounted * p);
void intrusive_ptr_release(CRefCounted * p);
class CRefCounted
friend void ::boost::intrusive_ptr_add_ref(CRefCounted * p);
friend void ::boost::intrusive_ptr_release(CRefCounted * p);
CRefCounted() : references(<span style="color:#) {}
namespace boost
inline void intrusive_ptr_add_ref(CRefCounted * p)
++(p-&references);
inline void intrusive_ptr_release(CRefCounted * p)
if (--(p-&references) == <span style="color:#)
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
They are almost identical to scoped_ptr andshared_ptr - only they act like pointers
to arrays, i.e., like pointers that were allocated usingoperatornew[].
They provide an overloadedoperator[]. Note that neither of them knows the length initially allocated.
Download the current boost version from , and unzip it to a folder of your choice. The unzipped sources use the following structure (using my folders):
the actual boost sources / headers
the documentation of the current version, in HTML format
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.
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 (!).
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 andmax, and consequently, these respective
functions are missing from the (original) STL implementation. Some Windows libraries such as MFC rely onmin/max
being present. Boost, however, expectsmin andmax in thestd::
namespace. To make things worse, there is no feasiblemin/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 firstinclude:
Collapse |
#define _NOMINMAX
#include "boost/config.hpp"
using std::
using std::
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.
Not enough information? More Questions?
Questions about boost? That's the place to go.
Articles on Code Project:
by Andrew Walker
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.
Sept 05, 2004: Initial version
Sept 27, 2004: Published to CodeProject
Sept 29, 2004: Minor Fixes
This article, along with any associated source code and files, is licensed under
【上篇】【下篇】

我要回帖

更多关于 boost共享指针 的文章

 

随机推荐