C++ STL智能指针系列:
文章目录
前言
在上篇文章中,我们提到了unique_ptr,解决了一部分auto_ptr的问题,但是前面这两种指针都是基于排他所有权模式,无法被多个对象托管。为应对这个问题,shared_ptr出现了。
std::shared_ptr
shared_ptr是最常用的智能指针。shared_ptr使用了引用计数(reference counting),也就是说每引用一次,引用计数就会加一,而一个生命周期结束又会减一。等到这个计数为0时,就delete。这就防止了多次析构的问题。
下面的都是两次访问都是合法的,因为shared_ptr有引用计数,就不存在所有权的问题了:
shared_ptr<Object> pObj1(new Object());
shared_ptr<Object> pObj2 = pObj1;
cout<<pObj1->num<<endl;
cout<<pObj2->num<<endl;
shared_ptr用法
使用std::make_shared函数创建对象
官方建议使用make_shared
创建对象,主要原因是:
1.创建方便
对比于传统的默认构造函数:
shared_ptr<Object> pObj(new Object());
使用make_shared更加方便
auto pObj = make_shared<Object>();
2.减少分配次数
传统方法要分配一次new Object()
再分配一次shared_ptr控制块,而make_ptr直接一次分配了shared_ptr控制块。
3.异常安全
假设我现在有一个函数function,我需要传入两个shared_ptr:
void function(const shared_ptr<Object1> &pObj1,
const shared_ptr<Object2> &pObj2);
使用时:
function(shared_ptr<Object1>(new Object1("I'm obj1")),
shared_ptr<Object2>(new Object2("I'm obj2")));
由于C++允许按任意顺序执行子表达式(C++17之前,所以C++17之后就不存在这个问题)。其中一个可能的执行顺序是:
-
new Object1("I'm obj1")
-
new Object2("I'm obj2")
-
std::shared_ptr<Object1>
-
std::shared_ptr<Object2>
假设在第2步抛出了异常,我们就会丢失在第一步分配的内存, 这块内存回收不了, 就导致了内存泄露。
此问题的解决方法就是使用make_shared
,他只用一次分配,就不存在前面的那种情况:
function(std::make_shared<Object1>("I'm obj1"),
std::make_shared<Object2>("I'm obj2"));
其实还有一种解决方法,就是将其拆出来,分配后再传入:
shared_ptr<Object1> pObj1(new Object1("I'm obj1"));
shared_ptr<Object2> pObj2(new Object2("I'm obj2"));
function(pObj1, pObj2);
但是这种方法明显繁琐许多,所以我们还是建议使用make_shared
。
Notice:
make_shared无法传入deleter,如果要使用自定义的deleter,还是只有使用默认的构造函数。
常用成员方法
其中的修改器reset
、swap
与unique_ptr相同,shared_ptr舍弃了release方法,因为其实reset()
可以直接替代release()
;
观察器get
、operator* & operator->
、operator[]
和operator bool
也都相同。此外,shared_ptr还提供了其他的观察器。
1. use_count()方法
此函数返回引用计数,可以帮助我们查看被几个shared_ptr所托管:
auto pObj1 = make_shared<Object>();
auto pObj2 = pObj1;
auto pObj3 = pObj1;
cout<<pObj1.use_count()<<endl;//pObj1,pObj2,pObj3效果都相同
输出结果:
3
2. unique()方法
这个函数C++17就弃用了,C++20移除了它,这个函数的作用是检测是否只有一个shared_ptr托管,相当于use_count()==1
自定义删除器
shared_ptr也可以自定义删除器,和unique_ptr一样。在unique_ptr我们讲了伪函数与函数作为删除器,这里我们补充一下lambda表达式作为删除器,此方法也同样适用于unique_ptr.
比如对于:
class Deleter
{
public:
void operator() (Object * p) {
delete[] p;
}
};
shared_ptr<Object> pObj(new Object[5], Deleter());
使用lambda表达式:
shared_ptr<Object> pObj(new Object[5], [](Object* p){
delete[] p;
});