boost.smart_ptr库提供了六种智能指针,包括scoped_ptr、scoped_array、shared_ptr、shared_array、weak_ptr和intrusive_ptr。为了使用smart_ptr组件,需要包含头文件<boost/smart_ptr.hpp>。
Scoped_ptr
scoped_ptr是一个很类似auto_ptr/unique_ptr的智能指针,但scoped_ptr的所有权更加严格,不能转让,一旦scoped_ptr获取了对象的管理权,我们就无法再从它那里取回来。与普通拇指针相比,它只有很少的接口,这一点使指针的使用更加安全,更容易使用同时更不容易被误用。
#include <iostream>
#include <string>
#include <boost/smart_ptr.hpp>
using namespace std;
using namespace boost;
int main()
{
/* scoped_ptr的基本用法 */
scoped_ptr<string> sp(new string("hello world"));
cout << "content: " << *sp << ", length: " << sp->size() << endl;
/* 无法对scoped_ptr进行拷贝构造 */
//scoped_ptr<string> sp2 = sp;
/* 在*和->之外scoped_ptr没有定义其他操作符 */
//sp++; //错误,scoped_ptr未定义递增操作
//prev(sp); //错误,scoped_ptr未定义递减操作
return 0;
}
输出:content: hello world, length: 11
scoped_array
scoped_array很像scoped_ptr,它包装了new[]操作符在堆上分配的动态数组,为动态数组提供了一个代理,保证可以正确地释放内存。但scoped_array的功能很有限,不能动态增长,没有边界检查,也没有迭代器支持,不能搭配STL算法。而且,我们应当避免使用new[]运算符,在需要动态数组的情况下我们应该使用std::vector。
#include <iostream>
#include <boost/smart_ptr.hpp>
using namespace std;
using namespace boost;
int main()
{
/* scoped_array的基本用法 */
int *arr = new int[10];
scoped_array<int> sa(arr);
fill_n(&sa[0], 10, 5);
sa[1] = sa[2] + sa[3];
for (int i = 0; i < 10; ++i)
cout << sa[i] << " ";
/* scoped_array不提供指针运算 */
//sa[0] = 10; //正确
//*(sa + 1) = 20; //错误
return 0;
}
输出:
5 10 5 5 5 5 5 5 5 5
shared_ptr
shared_ptr是boost.smart_ptr库中最有价值、最重要的组成部分,它被毫无悬念地收入了C++11标准。shared_ptr实现的是引用计数型指针,可以被自由地拷贝和赋值,在任意的地方共享它,当引用计数为0时它才删除被包装的动态分配的对象。
基本用法
#include <iostream>
#include <boost/smart_ptr.hpp>
using namespace boost;
using std::cout;
using std::endl;
int main()
{
shared_ptr<int> sp(new int);
*sp = 10;
shared_ptr<int> sp2 = sp;
if (!sp.unique())
cout << "sp不是指针的唯一的持有者\n";
cout << "引用计数为" << sp.use_count() << endl;
sp2.reset(); //将引用计数减一
if (sp.unique())
cout << "现在sp是指针的唯一的持有者\n";
return 0;
}
输出:sp不是指针的唯一的持有者
引用计数为2
现在sp是指针的唯一的持有者
引用计数为2
现在sp是指针的唯一的持有者
工厂函数
smart_ptr库提供了一个工厂函数make_shared()来消除显示的new调用,声明如下:template< class T, class... Args > typename boost::detail::sp_if_not_array< T >::type make_shared( Args && ... args );
通常make_shared()函数要比直接创建shared_ptr对象的方式快且高效,因为它内部仅分配一次内存,消除了shared_ptr构造时的开销。
例如:
auto sp = make_shared<string>("make_shared");
应用于标准容器
可以将shared_ptr作为容器的元素,如vector<shared_ptr<T>>,因为shared_ptr支持拷贝语义和比较操作,符合标准容器对元素的要求,所以可以在容器中安全地容纳元素的指针而不是拷贝。标准容器不能容纳scoped_ptr,因为scoped_ptr不能拷贝和赋值。
#include <iostream>
#include <vector>
#include <boost/smart_ptr.hpp>
using namespace boost;
using std::cout;
using std::endl;
using std::vector;
int main()
{
typedef vector<shared_ptr<int>> vs;
vs v(10);
int i = 0;
for (auto pos = v.begin(); pos != v.end(); ++pos) {
(*pos) = make_shared<int>(++i);
cout << *(*pos) << ", ";
}
cout << endl;
shared_ptr<int> p = v[9];
*p = 100;
cout << *v[9] << endl;
return 0;
}
输出:
1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
100
100
定制删除器
shared_ptr有这样的一个构造函数:share_ptr(Y * p, D d)。它的第一参数是要被管理的指针,它的含义与其他构造函数的参数相同。而第二个删除器参数d则告诉shared_ptr在析构时不是使用delete来操作指针p,而要用d来操作,即把delete p换成d(p)。
在这里删除器d可以是一个函数对象,也可以是一个函数指针,只要它能够像函数那样被调用,使得d(p)成立即可。对删除器的要求是它必须可拷贝,行为必须也像delete那样,不能抛出异常.
假设我们有一组操作socket的函数,使用一个socket_t类:
class socket_t {...};
socket_t *open_socket()
{
cout << "open_socket" << endl;
return new socket_t;
}
void close_socket(socket_t *s)
{
cout << "close_socket" << endl;
...
}
...
}
那么,socket资源对应的释放操作就是函数close_socket(),它符合shared_ptr对删除器的定义,可以用shared_ptr这样管理socket资源:
socket_t *s = open_socket();
shared_ptr<socket_t> p(s, close_socket);
//传入删除器
shared_array
shared_array类似shared_ptr,它包装了new[]操作符在堆上分配的动态数组,它就像是shared_ptr和scoped_array的结合体,既具有shared_ptr的优点,也具有scoped_array的缺点。
#include <iostream>
#include <boost/smart_ptr.hpp>
using namespace boost;
int main()
{
int *p = new int[10];
shared_array<int> sa(p);
if (sa.unique())
std::cout << "sa唯一持有指针\n";
shared_array<int> sa2 = sa;
std::cout << "现在,引用计数为" << sa.use_count() << std::endl;
sa[0] = 10;
std::cout << "sa2[0] = " << sa2[0];
return 0;
}
输出:
sa唯一持有指针
现在,引用计数为2
sa2[0] = 10
现在,引用计数为2
sa2[0] = 10
weak_ptr
weak_ptr是为配合shared_ptr而引入的一种智能指针,它更像是sharead_ptr的一个助手而不是智能指针,因为它不具有普通指针的行为,没有重载operator*和->。它的最大作用在于协助shared_ptr工作,像旁观者那样观测资源的使用情况。但它可以使用一个非常重要的成员函数lock()从被观测的shared_ptr获得一个可用的shared_ptr对象,把弱关系转换为强关系
#include <iostream>
#include <vector>
#include <boost/smart_ptr.hpp>
using namespace boost;
using std::cout;
using std::endl;
int main()
{
shared_ptr<int> sp(new int[10]);
weak_ptr<int> wp(sp);
cout << "引用计数:" << wp.use_count() << endl; //weak_ptr不影响引用计数
if (!wp.expired()) { //相当于wp.use_count() == 1
shared_ptr<int> sp2 = wp.lock();
cout << "现在,引用计数为" << wp.use_count() << endl;
}
sp.reset(); //shared_ptr失效
cout << "引用计数为" << wp.use_count() << endl;
return 0;
}
输出:
引用计数:1
现在,引用计数为2
引用计数为0
现在,引用计数为2
引用计数为0
intrusive_ptr
intrusive_ptr也是引用计数型指针,所以它的接口与shared_ptr很像,但它自己不直接管理引用计数,而是调用下面两个函数来间接管理:
void intrusive_ptr_add_ref(T * p); //增加引用计数
void intrusive_ptr_release(T * p); //减少引用计数
intrusive_ptr的构造函数和reset()还多出一个add_ref参数,表示是否增加引用计数,如果add_ref==true,那么它就相当于weak_ptr,只是简单地观察对象。
#include <iostream>
#include <vector>
#include <boost/smart_ptr.hpp>
using namespace boost;
using std::cout;
using std::endl;
struct counted_data
{
// ...
int m_count = 0;
~counted_data()
{
cout << "dtor" << endl;
}
};
void intrusive_ptr_add_ref(counted_data* p)
{
++p->m_count;
}
void intrusive_ptr_release(counted_data* p)
{
if(--p->m_count == 0)
delete p;
}
int main()
{
typedef intrusive_ptr<counted_data> counted_ptr;
counted_ptr p(new counted_data);
cout << p->m_count << endl; // 1
counted_ptr p2(p);
cout << p2->m_count << endl; // 2
counted_ptr weak_p(p.get(), false);
cout << weak_p->m_count << endl; // 2
p2.reset();
cout << p->m_count << endl; // 1
return 0;
}
输出:1
2
2
1
dtor
为了简化实现引用计数的工作,intrusive_ptr在头文件<boost/smart_ptr/intrusive_ref_counter.hpp>里定义了一个辅助类intrusive_ref_counter,它需要被继承使用,这样子类就会自动获取引用计数的能力。
2
2
1
dtor
为了简化实现引用计数的工作,intrusive_ptr在头文件<boost/smart_ptr/intrusive_ref_counter.hpp>里定义了一个辅助类intrusive_ref_counter,它需要被继承使用,这样子类就会自动获取引用计数的能力。
#include <iostream>
#include <boost/smart_ptr.hpp>
#include <boost/smart_ptr/intrusive_ref_counter.hpp>
using namespace boost;
using std::cout;
using std::endl;
struct counted_data2 : public intrusive_ref_counter<counted_data2>
{
~counted_data2()
{
cout << "dtor2" << endl;
}
};
int main()
{
typedef intrusive_ptr<counted_data2> counted_ptr;
counted_ptr p(new counted_data2);
cout << p->use_count() << endl;
return 0;
}
输出:1
dtor2
dtor2