Boost 只能指针常用的有四种,分别为:shared_ptr、weak_ptr、auto_ptr以及scoped_ptr指针。
shared_ptr指针
shared_ptr指针就是所谓的只能计数指针,用来管理非栈上的内存指针。它可以从一个裸指针、另一个shared_ptr、一个auto_ptr、或者一个weak_ptr构造。还可以传递第二个参数给shared_ptr的构造函数,它被称为删除器(deleter)。删除器用于处理共享资源的释放,这对于管理那些不是用new分配也不是用delete释放的资源时非常有用。shared_ptr被创建后,就可以像普通指针一样使用了,除了一点,它不能被显式地删除。
shared_ptr指针的使用如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14{
shared_ptr<int> pInt1;
assert(pInt1.use_count() == 0); // 还没有引用指针
{
shared_ptr<int> pInt2(new int(5));
assert(pInt2.use_count() == 1); // new int(5)这个指针被引用1次
pInt1 = pInt2;
assert(pInt2.use_count() == 2); // new int(5)这个指针被引用2次
assert(pInt1.use_count() == 2);
} //pInt2离开作用域, 所以new int(5)被引用次数-1
assert(pInt1.use_count() == 1);
} // pInt1离开作用域,引用次数-1,现在new int(5)被引用0次,所以销毁它
如果资源的创建销毁不是以new和delete的方式进行的,该怎么办呢?通过前面的接口可以看到,shared_ptr的构造函数中可以指定删除器。示例代码如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14class FileCloser
{
public:
void operator()(FILE \*pf)
{
if (pf != NULL)
{
fclose(pf);
pf = NULL;
}
}
};
shared_ptr<FILE> fp(fopen(pszConfigFile, "r"), FileCloser());
在使用shared_ptr时,需要避免同一个对象指针被两次当成shard_ptr构造函数里的参数的情况。考虑如下代码:
1 | { |
正确的做法是将原始指针赋给智能指针后,以后的操作都要针对智能指针了。参考代码如下:1
2
3
4
5
6{
shared_ptr<int> temp1(new int(5));
assert(temp1.use_count() == 1);
shared_ptr<int> temp2(temp1);
assert(temp2.use_count() == 2);
} // temp1和temp2都离开作用域,引用次数变为0,指针被销毁。
另外,使用shared_ptr来包装this时,也会产生与上面类似的问题。考虑如下代码:1
2
3
4
5
6
7
8
9
10
11class A
{
public:
shared_ptr<A> Get()
{
return shared_ptr<A>(this);
}
}
shared_ptr<A> pA(new A());
shared_ptr<A> pB = pA->Get();
当pA和pB离开作用域时,会将堆上的对象释放两次。如何解决上述问题呢?C++ 11提供了如下机制:将类从enable_shared_from_this类派生,获取shared_ptr时使用shared_from_this接口。参考代码如下:1
2
3
4
5
6
7
8class A :public enable_shared_from_this<A>
{
public:
shared_ptr<A> Get()
{
return shared_from_this();
}
}
在多线程中使用shared_ptr时,如果存在拷贝或赋值操作,可能会由于同时访问引用计数而导致计数无效。解决方法是向每个线程中传递公共的week_ptr,线程中需要使用shared_ptr时,将week_ptr转换成shared_ptr即可。
以上例子来自这里
weak_ptr指针
weak_ptr是为配合shared_ptr而引入的一种智能指针,它更像是shared_ptr的一个助手,而不是智能指针,因为它不具有普通指针的行为,没有重载operator*和operator->,它的最大作用在于协助shared_ptr,像旁观者那样观测资源的使用情况。
1 | template<class T> class weak_ptr{ |
weak_ptr是一个“弱”指针,但它能够完成一些特殊的工作,足以证明它的存在价值。
weak_ptr被设计为与shared_ptr共同工作,可以从一个shared_ptr或者另一个weak_ptr对象构造,获得资源的观测权。但weak_ptr没有共享资源,它的构造不会引起指针引用计数的增加。同样,在weak_ptr析构时也不会导致引用计数的减少,它只是一个静静地观察者。
使用weak_ptr的成员函数use_count()可以观测资源的引用计数,另一个成员函数expired()的功能等价于use_count() == 0,但更快,表示观测的资源(也就是shared_ptr管理的资源)已经不复存在了。
weak_ptr 没有重载operator*和->,这是特意的,因为它不共享指针,不能操作资源,这是它弱的原因。但它可以使用一个非常重要的成员函数lock()从被观测的shared_ptr获得一个可用的shared_ptr对象,从而操作资源。当expired() == true的时候,lock()函数将返回一个存储空指针的shared_ptr。
1 | int main(){ |
获得this的shared_ptr
weak_ptr的一个重要用途是获得this指针的shared_ptr,使对象自己能够生产shared_ptr管理自己:对象使用weak_ptr观测this指,这并不影响引用计数,在需要的时候就调用lock()函数,返回一个符合要求的shared_ptr使外界使用。
这个解决方案被实现为一个惯用法,在头文件1
2
3
4
5
6template<class T>
class enable_shared_from_this
{
public:
shared_ptr<T> shared_from_this();
}
使用的时候只需要让想被shared_ptr管理的类从它继承即可,成员函数shared_from_this()会返回this的shared_ptr
1 | #include <iostream> |
运行结果:1
2self_shared:315
self_shared:100
需要注意的是千万不能从一个普通对象(非shared_ptr)使用shared_from_this ()获取shared_ptr,如1
2
3self_shared ss;
shaerd_ptr<self_shared> p = ss.shared_from_this();//error
这样虽然语法上能通过,编译也无问题,但在运行时会导致shared_ptr析构时企图删除一个栈上分配的对象,发生未定义行为。
以上内容来自这里
auto_ptr指针
auto_ptr通过在栈上构建一个对象a,对象a中wrap了动态分配内存的指针p,所有对指针p的操作都转为对对象a的操作。而在a的析构函数中会自动释放p的空间,而该析构函数是编译器自动调用的,无需程序员操心。
1 | 用法一: |
对于上面的代码:m_example2=m_example1; 则C++会把m_example所指向的内存回收,使m_example1 的值为NULL,所以在C++中,应绝对避免把auto_ptr放到容器中。即应避免下列代码:
vector
当用算法对容器操作的时候,你很难避免STL内部对容器中的元素实现赋值传递,这样便会使容器中多个元素被置位NULL,而这不是我们想看到的。
示例:1
2
3
4
5
6
7// 示例1(a):原始代码
void f()
{
T* pt( new T );
/*...更多的代码...*/
delete pt;
}
以上代码需要手动的delete掉堆上的内存,如果使用auto_ptr指针的话,就不需要:1
2
3
4
5
6// 示例1(b):安全代码,使用了auto_ptr
void f()
{
auto_ptr<T> pt( new T );
/*...更多的代码...*/
} // 酷:当pt出了作用域时析构函数被调用,从而对象被自动删除
使用一个auto_ptr就像使用一个内建的指针一样容易,而且如果想要“撤销”资源,重新采用手动的所有权,我们只要调用release()。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19// 示例2:使用一个auto_ptr
void g()
{
// 现在,我们有了一个分配好的对象
T* pt1 = new T;
// 将所有权传给了一个auto_ptr对象
auto_ptr<T> pt2(pt1);
// 使用auto_ptr就像我们以前使用简单指针一样,
*pt2 = 12; // 就像*pt1 = 12
pt2->SomeFunc(); // 就像pt1->SomeFunc();
// 用get()来获得指针的值
assert( pt1 == pt2.get() );
// 用release()来撤销所有权
T* pt3 = pt2.release();
// 自己删除这个对象,因为现在没有任何auto_ptr拥有这个对象
delete pt3;
} // pt2不再拥有任何指针,所以不要试图删除它...OK,不要重复删除
```
我们可以使用auto_ptr的reset()函数来重置auto_ptr使之拥有另一个对象。如果这个auto_ptr已经拥有了一个对象,那么,它会先删除已经拥有的对象,因此调用reset()就如同销毁这个auto_ptr,然后新建一个并拥有一个新对象:1
2
3
4
5
6// 示例 3:使用reset()
void h()
{
auto_ptr<T> pt( new T(1) );
pt.reset( new T(2) ); // 删除由"new T(1)"分配出来的第一个T
} // 最后pt出了作用域,第二个T也被删除了
以上原文来自这里
scoped_ptr指针
scoped_ptr和std::auto_ptr非常类似,是一个简单的智能指针,它能够保证在离开作用域后对象被自动释放。下列代码演示了该指针的基本应用:
1 | #include <string> |
该代码的输出结果是:
1 | Test Begin ... |
可以看到:当implementation类离其开impl作用域的时候,会被自动删除,这样就会避免由于忘记手动调用delete而造成内存泄漏了。
scoped_ptr的实现和std::auto_ptr非常类似,都是利用了一个栈上的对象去管理一个堆上的对象,从而使得堆上的对象随着栈上的对象销毁时自动删除。不同的是,boost::scoped_ptr有着更严格的使用限制——不能拷贝。这就意味着:boost::scoped_ptr指针是不能转换其所有权的。
不能转换所有权
boost::scoped_ptr所管理的对象生命周期仅仅局限于一个区间(该指针所在的”{}”之间),无法传到区间之外,这就意味着boost::scoped_ptr对象是不能作为函数的返回值的(std::auto_ptr可以)。不能共享所有权
这点和std::auto_ptr类似。这个特点一方面使得该指针简单易用。另一方面也造成了功能的薄弱——不能用于stl的容器中。不能用于管理数组对象
由于boost::scoped_ptr是通过delete来删除所管理对象的,而数组对象必须通过deletep[]来删除,因此boost::scoped_ptr是不能管理数组对象的,如果要管理数组对象需要使用boost::scoped_array类。
scoped_ptr的定义:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20namespace boost {
template<typename T> class scoped_ptr : noncopyable {
public:
explicit scoped_ptr(T* p = 0);
~scoped_ptr();
void reset(T* p = 0);
T& operator*() const;
T* operator->() const;
T* get() const;
void swap(scoped_ptr& b);
};
template<typename T>
void swap(scoped_ptr<T> & a, scoped_ptr<T> & b);
}
```
示例:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38#include <string>
#include <iostream>
#include <boost/scoped_ptr.hpp>
#include <boost/scoped_array.hpp>
#include <boost/config.hpp>
#include <boost/detail/lightweight_test.hpp>
void test()
{
// test scoped_ptr with a built-in type
long * lp = new long;
boost::scoped_ptr<long> sp ( lp );
BOOST_TEST( sp.get() == lp );
BOOST_TEST( lp == sp.get() );
BOOST_TEST( &\*sp == lp );
\*sp = 1234568901L;
BOOST_TEST( \*sp == 1234568901L );
BOOST_TEST( \*lp == 1234568901L );
long * lp2 = new long;
boost::scoped_ptr<long> sp2 ( lp2 );
sp.swap(sp2);
BOOST_TEST( sp.get() == lp2 );
BOOST_TEST( sp2.get() == lp );
sp.reset(NULL);
BOOST_TEST( sp.get() == NULL );
}
void main()
{
test();
}
boost::scoped_ptr和std::auto_ptr的选取:
boost::scoped_ptr和std::auto_ptr的功能和操作都非常类似,如何在他们之间选取取决于是否需要转移所管理的对象的所有权(如是否需要作为函数的返回值)。如果没有这个需要的话,大可以使用boost::scoped_ptr,让编译器来进行更严格的检查,来发现一些不正确的赋值操作。
以上原文来自这里