shared_ptr主要是为了避免操心指针指向的资源,保证在最后一个对象结束生命的时候其相应资源也被释放,但有些情形并不期望这样或无法运作:
1、环式指向:如果两对象使用shared_ptr互相指向对方,而一旦不存在外部引用指向他们时,这种情况下shared_ptr不会释放数据,因为每个对象的use_count()仍是1;
#include <iostream>
#include <string>
#include <utility>
#include <memory>
using std::cout;
using std::cin;
using std::endl;
class StructB;
class StructA
{
public:
StructA() : m_pb(nullptr)
{
cout << "Contruct A" << endl;
}
~StructA()
{
cout << "Delete A" << endl;
}
void setStructB(std::shared_ptr<StructB> p)
{
m_pb = p;
}
private:
std::shared_ptr<StructB> m_pb;
};
class StructB
{
public:
StructB():m_pa(nullptr)
{
cout << "Contruct B" << endl;
}
~StructB()
{
cout << "Delete B" << endl;
}
void setStructA(std::shared_ptr<StructA> p)
{
m_pa = p;
}
private:
std::shared_ptr<StructA> m_pa;
};
int main()
{
std::shared_ptr<StructA> a;
std::shared_ptr<StructB> b;
a.reset(new StructA);
b.reset(new StructB);
a->setStructB(b);
b->setStructA(a);
return 0;
}
最终程序输出:
2、引用的寿命比所指对象寿命更长:此时shared_ptr绝不释放对象,而寻常的指针可能不会注意到他们所指向的对象已经不再有效,导致“访问已被释放的数据”的风险。
因此标准库提供了weak_ptr,允许“共享但不拥有”某对象。
一旦最后一个对象释放掉以后,所有的weak pointer都将为空,因此,除了默认和复制构造函数之外,weak_ptr只提供接受一个shared_ptr的构造函数。
注:无法通过*和->访问weak_ptr指向的对象,而是必须建立另一个shared pointer。这样做基于以下两个理由:
1、在weak pointer之外建立一个shared pointer可检查是否仍存在一个响应对象。如果不存在,会抛出异常或建立一个空的shared_pointer
2、当指向的对象正被处理时,shared pointer无法被释放。基于以上:weak_ptr只提供少量操作:创建、复制、赋值以及转换成一个shared_ptr,或检查自己是否指向对象
使用weak pointer时,我们需要改变指向对象的访问方式:
p->mother->kids[0]->name; //error
p->mother->kids[0].lock()->name;//ok
调用lock()会导致产生一个shared_ptr,这样的话,就有可能会产生一个空的shared_ptr,如果对象的最后拥有者已经被释放了的话。这种时候可以通过以下选择判断weak_ptr背后的对象是否还存活着:
1、expired():会在wea_ptr不再共享对象时返回true
2、使用shared_ptr构造函数将weak_ptr转换成一个shared_ptr,如果对象不存在,会抛出一个异常bad_weak_ptr
3、调用use_count():这通常只是为了调试使用,c++标准库明确指出“use_count()并不总是很有效率”