16.1 C++智能指针-new/delete探秘
16.2 C++智能指针-shared_ptr
16.3 C++智能指针-weak_ptr
16.4 C++智能指针-shared_ptr使用场景、陷阱、性能分析与使用建议
16.5 C++智能指针-unique_ptr
weak_ptr这个智能指针是用来辅助shared_ptr工作的。那么,现在就来介绍一下weak_ptr
3.weak_ptr
3.1 weak_ptr简介
weak_ptr是一个智能指针,也是一个类模板。这个智能指针指向一个由shared_ptr管理的对象,但是这种指针并不控制所指向的对象的生存期。换句话来说,将weak_ptr绑定到shared_ptr上并不会改变shared_ptr的引用计数(更确切地说,weak_ptr的构造和析构不会增加或者减少所指向对象的引用计数)。当shared_ptr需要释放所指向的对象时照常释放,不管是否有weak_ptr指向该对象。这就是weak“弱”的原因——能力弱(弱共享/弱引用:共享其他的shared_ptr所指向的对象),控制不了所指向对象的生存期。
创建weak_ptr的时候,一般是用一个make_shared来初始化
前面谈到的shared_ptr指向的对象代表的引用统统指的都是强引用,而weak_ptr所指向的对象代表的引用统统都是弱引用。
weak_ptr所指向的对象有可能会不存在,所以,程序员是不能使用weak_ptr来直接访问对象的,必须要使用一个叫作lock的成员函数,lock的功能就是检查weak_ptr所指向的对象是否还存在,如果存在,lock能够返回一个指向共享对象的shared_ptr(当然原shared_ptr引用计数会+1),如果不存在,则返回一个空的shared_ptr。
{
auto pi = make_shared<int>(100);
weak_ptr<int> piw(pi); //piw弱共享pi,pi引用计数(强引用计数)不改变,弱引用计数字会从0变成1;pi和piw两者指向相同位置
//weak_ptr<int> piw;
piw = pi; //pi这里是一个shared_ptr,赋值给一个weak_ptr。pi和piw两者指向相同位置
weak_ptr<int> piw2;
piw2 = piw; //把weak_ptr赋给另外一个weak_ptr,现在pi是1个强引用,2个弱引用
auto pi2 = piw.lock(); //强引用(shared_ptr)计数会加1,现在pi是2个强引用,2个弱引用
if (pi2 != nullptr) //条件成立;写成if(pi2)也可以
{
cout << "所指对象存在" << endl;
}
}
{
auto pi = make_shared<int>(100);
weak_ptr<int> piw(pi); //piw弱共享pi,pi强引用计数不改变,弱引用计数字会从0变成1
pi.reset(); //因为pi是唯一指向该对象的指针,则释放pi指向的对象,将pi置空
auto pi2 = piw.lock(); //因为所指向的对象被释放了,所以piw弱引用也属于“过期”的了
if (pi2 != nullptr) //条件不再成立
{
cout << "所指对象存在" << endl;
}
}
3.2 weak_ptr常用操作
(1)use_count成员函数
获取与该弱指针共享对象的其他shared_ptr的数量,或者说获得当前所观测资源的引用计数(强引用计数)
(2)expired成员函数
是否过期的意思,若该指针的use_cout为0(表示该弱指针所指向的对象已经不存在),则返回true,否则返回false。换句话说,判断所观测的对象(资源)是否已经被释放。
{
auto pi = make_shared<int>(100);
auto pi2(pi); //pi2类型是个shared_ptr
weak_ptr<int> piw(pi);
int isc = piw.use_count(); //2:与本piw共享对象的shared_ptr数量
pi.reset();
pi2.reset();
if (piw.expired()) //是否过期,此时成立
{
cout << "piw已过期" << endl;
}
}
{
auto p1 = make_shared<int>(42);
weak_ptr<int> pw;
pw = p1; //可以用shared_ptr给weak_ptr值,现在p1是1个强引用,1个弱引用
if (!pw.expired()) //条件成立
{
//没过期:
auto p2 = pw.lock(); //返回的p2是一个shared_ptr,现在p1是2个强引用,1个弱引用
if (p2 != nullptr)//条件成立
{
cout << "所指对象存在" << endl;
}
//离开这个范围,p1的强引用计数恢复为1,弱引用保持为1
}
else
{
cout << "pw已经过期" << endl;
}
//走到这里,p1是1个强引用,1个弱引用
}
{
weak_ptr<int> pw;
{
auto p1 = make_shared<int>(42);
pw = p1; //可以用shared_ptr给weak_ptr值
} //离开这里时p1就失效了,那么pw会变成啥情况?
//这里pw这个weak_ptr就会过期了
if (pw.expired()) //条件成立
{
cout << "pw已经过期" << endl;
}
}
(3)尺寸问题
其实weak_ptr尺寸(就是大小或者sizeof)和shared_ptr对象尺寸一样大,后续章节里会提到shared_ptr的尺寸问题,这里先提一下weak_ptr对象尺寸问题。
{
shared_ptr<int> p1(new int(100));
weak_ptr<int> pw(p1);
int ilen = sizeof(p1); //8
int ilen2 = sizeof(pw); //8
cout << "设置断点" << endl;
}
在当前Visual Studio的x86平台下,一个裸指针的sizeof值是4字节。从上面代码可以看到,weak_ptr或shared_ptr的尺寸都是8字节,其实,这8字节中包含了两个裸指针
可以看到:
(1)第一个裸指针指向的是该智能指针所指向的对象。
(2)第二个裸指针指向一个很大的数据结构(控制块)。这个控制块里面有:
所指对象的引用计数。
所指对象的弱引用计数。
其他数据,如自定义的删除器的指针(如果指定了自定义删除器)等。
控制块实际是由shared_ptr创建出来的,而后,当使用shared_ptr对象创建weak_ptr对象时,weak_ptr对象也指向了这个控制块。具体后面还会讲到shared_ptr的控制块,到时再细讲。