shared_ptr
内部包含两个指针,一个指向对象,另一个指向控制块,控制块中包含一个引用计数和其它一些数据。由于这个控制块需要在多个shared_ptr
之间共享,即多个shared_ptr
可以共享同一块内存,所以它也是存在于 heap 中的。
如果没有共享所有权的必要,就不必使用shared_ptr
,而非unique_ptr
由于支持共享所有权,shared_ptr
支持拷贝和赋值运算, 也支持移动。如果对象在创建的时候没有使用共享指针存储的话,之后也不能用共享指针管理这个对象了。
避免使用原生指针来创建shared_ptr
指针
shared_ptr
销毁对象的情况:
- 最后一个智能指针离开作用域
- 用其他的
shared_ptr
给一个shared_ptr
初始化 - 最后一个智能指针调用 reset
shared_ptr
的缺点:
内存占用是原生指针的两倍,因为除了要管理一个原生指针外,还要维护一个引用计数
使用了性能低的原子操作:考虑到线程安全问题,引用计数的增减必须是原子操作。 而原子操作一般情况下都比非原子操作慢
两个
shared_ptr
可能出现循环引用,永远不能释放指向的对象
线程安全性
shared_ptr
有两个成员,指向对象的指针ptr和管理引用计数的指针ref_count。引用计数本身是原子操作,是线程安全的,但 shared_ptr
的赋值操作由复制对象指针和修改使用计数两个操作复合而成, 因此仍不是线程安全的。如果要从多个线程读写同一个shared_ptr
对象,还是需要加锁。
陈硕专门写了这篇文章分析这个问题,
也可以看我自己的这篇文章,子线程里能写shared_ptr指向的对象,回到主线程就变了。
尽量使用 make_shared()
为了节省一次内存分配,原来shared_ptr<Foo> x(new Foo);
需要为 Foo 和 ref_count 各分配一次内存,现在用make_shared()
的话,可以一次分配一块足够大的内存,供 Foo 和 ref_count 对象容身。
常见用法
1 | int num = 12; |
p1的用法是错的,p2和p3正确,但是不要同时使用,改用p3(p2)即可
1 | boost::shared_ptr<Foo> a; |
执行a->out()
会报错,原因是a没有指向对象,应该这样定义:boost::shared_ptr<Foo> a(new Foo());
再看这样的代码:1
2
3
4
5
6
7
8
9
10
11
12
13
14boost::shared_ptr<Foo> a(new Foo()); // 让a指向对象
cout<< a.use_count()<<endl; // 1
boost::shared_ptr<Foo> b = a; // 另一个指针也指向同一个对象
cout<< a.use_count()<<endl; // 2
cout<< b.use_count()<<endl; // 2
a.reset(); // 不执行析构函数,实际执行 delete a; a = NULL;
a->out(); // 报错
b->out(); // 正常
cout<< a.use_count()<<endl; // 0
cout<< b.use_count()<<endl; // 1
b.reset(); // 执行析构函数
cout<<"end"<<endl;
只有对象的引用计数为0的时候,才执行类的析构和free其内存:1
2
3
4
5
6
7
8
9boost::shared_ptr<Foo> a(new Foo());
boost::shared_ptr<Foo> b = a;
cout<<a<<endl;
cout<<b<<endl;
a.reset(new Foo()); // a重新初始化,指向另一个地址
cout<<a<<endl;
cout<<b<<endl;
运行结果:1
2
3
4
50x99fc20
0x99fc20
0x9a0c70
0x99fc20
不要把一个原生指针给多个shared_ptr管理1
2
3int* ptr = new int;
boost::shared_ptr<int> p1(ptr);
boost::shared_ptr<int> p2(ptr);
这样做会导致ptr会被释放两次。在实际应用中,保证除了第一个shared_ptr
使用ptr定义之外,后面的都采用p1来操作,就不会出现此类问题。
可以在标准容器里存储boost::shared_ptr,但不能存储std::auto_ptr
和boost::scoped_ptr
,后两者不能共享对象所有权.1
2
3std::vector<boost::shared_ptr<int> > v;
v.push_back(boost::shared_ptr<int>(new int(1)));
v.push_back(boost::shared_ptr<int>(new int(2)));
自定义删除器
默认情况下,shared_ptr调用delete()
函数进行资源释放,即delete p;
。但是如果shared_ptr指向一个数组而不是一个简单的指针,应该调用delete[] p
,此时可以将一个回调传递给shared_ptr的构造函数来定制删除器。
主要是利用了构造函数template<class Y, class D> shared_ptr(Y * p, D d);
,第一个参数是要被管理的指针, 与其它形式的构造函数一致; 第二个参数称为删除器, 他是一个接受Y*
的可调用物, d(p)的行为应类似与delete p, 而且不应该抛出异常。有了删除器, 我们就可以管理一些更复杂的资源, 比如数据库连接, socket连接。
其实有很多种用法,只列举常用的普通函数法1
2
3
4
5
6
7
8
9Derived *d = new Derived[5];
boost::shared_ptr<Derived> p1(d, deleter);
// deleter函数定义
void deleter(Derived* d)
{
delete[] d;
cout<<"delete"<<endl;
}