目录
一,weak_ptr 变量的定义
二,expired() 成员函数
三,lock() 成员函数
四,use_count() 成员函数
五,为什么要用 weak_ptr
一,weak_ptr 变量的定义
weak_ptr 对象的构造有3种方法:
1,构造空对象,如 std::weak_ptr<CTest> weakPtr;
2,拷贝构造,如 std::weak_ptr<CTest> weakPtr2(weakPtr);
3,用shared_ptr 对象进行初始化,如
std::shared_ptr<CTest> ptr = std::make_shared<CTest>();
std::weak_ptr<CTest> weakPtr3(ptr);
#include <stdio.h>
#include <stdlib.h>
#include <memory>
class CTest
{
public:
CTest(): mValue(100)
{
printf("constructor\n");
}
~CTest()
{
printf("destructor\n");
}
int getValue()
{
return mValue;
}
private:
int mValue;
};
int main()
{
std::weak_ptr<CTest> weakPtr;
std::weak_ptr<CTest> weakPtr2(weakPtr);
std::shared_ptr<CTest> ptr = std::make_shared<CTest>();
std::weak_ptr<CTest> weakPtr3(ptr);
int value = ptr->getValue();
printf("value = %d, use_count = %d\n", value, ptr.use_count());
value = (weakPtr3.lock())->getValue();
printf("value = %d\n", value);
ptr.reset();
auto ptr2 = weakPtr3.lock();
if(ptr2 == nullptr)
{
printf("weakPtr3.lock() = nullptr\n");
return 0;
}
value = ptr2->getValue();
return 0;
}
二,expired() 成员函数
bool expired() const noexcept; 函数返回 weak_ptr 对象是否是空的,或是它所属的所有者组中不再有shared_ptr。此函数与 use_count() == 0 意义相同。
#include <stdio.h>
#include <stdlib.h>
#include <memory>
class CTest
{
public:
CTest(): mValue(100)
{
printf("constructor\n");
}
~CTest()
{
printf("destructor\n");
}
int getValue()
{
return mValue;
}
private:
int mValue;
};
int main()
{
std::weak_ptr<CTest> weakPtr;
std::weak_ptr<CTest> weakPtr2(weakPtr);
std::shared_ptr<CTest> ptr = std::make_shared<CTest>();
std::weak_ptr<CTest> weakPtr3(ptr);
if(weakPtr.expired())
{
printf("weakPtr is empty!\n");
}
if(weakPtr2.expired())
{
printf("weakPtr2 is empty!\n");
}
if(weakPtr3.expired())
{
printf("weakPtr3 is empty!\n");
}
return 0;
}
我们可以看到源码是这样的:
三,lock() 成员函数
shared_ptr<element_type> lock() const noexcept; 函数返回一个shared_ptr,其中包含weak_ptr对象在未过期时保留的信息。如果 weak_ptr 对象已过期(包括它是否为空),该函数将返回一个空的shared_ptr(就像默认构造的一样)。
#include <stdio.h>
#include <stdlib.h>
#include <memory>
class CTest
{
public:
CTest(): mValue(100)
{
printf("constructor\n");
}
~CTest()
{
printf("destructor\n");
}
int getValue()
{
return mValue;
}
private:
int mValue;
};
int main()
{
std::weak_ptr<CTest> weakPtr;
std::weak_ptr<CTest> weakPtr2(weakPtr);
std::shared_ptr<CTest> ptr = std::make_shared<CTest>();
std::weak_ptr<CTest> weakPtr3(ptr);
std::shared_ptr<CTest> lck;
lck = weakPtr.lock();
if(lck == nullptr)
{
printf("weakPtr is empty!\n");
}
if(!weakPtr2.lock())
{
printf("weakPtr2 is empty!\n");
}
std::shared_ptr<CTest> test;
if((test = weakPtr3.lock()) != nullptr)
{
printf("weakPtr3 is not empty! value = %d\n", test->getValue());
}
return 0;
}
在使用 lock() 函数时,可以判断其是否为nullptr。当用 gdb 查看时可以看到:lck=weakPtr.lock()后,lck 就是 0。
我们可以看源码 lock() 函数是怎样实现的:
可以看到当对象过期时,构造了一个空的 shared_ptr<>对象进行返回,而这个空对象它的初始化值是这样的:
_M_ptr 是模板参数类型的指针,它指向的就是要管理的对象。而 _M_refcount 是管理引用计数的。所以一个空的 shared_ptr<> 并不管理任何对象,就不能当作对象指针来用了。但 use_count() 还是可以用的,用 lck.use_count() == 0 进行判断:
这里可能会有人疑问:lck 都是nullptr 了,怎么还能调用 use_count() 呢?这样调用不会崩溃吗?这个就要理解类与对象的关系了。可以看源码:
四,use_count() 成员函数
weak_ptr 里有两个计数,一个是 _M_use_count 管理对象的引用计数,一个是 _M_weak_count 是不是用于它本身的析构用的,还没研究。weak_ptr 的基类:
_Tp* _M_ptr; // Contained pointer.
__weak_count<_Lp> _M_refcount; // Reference counter.
template<_Lock_policy _Lp>
class __weak_count
{
public:
constexpr __weak_count() noexcept : _M_pi(0)
{ }
__weak_count(const __shared_count<_Lp>& __r) noexcept
: _M_pi(__r._M_pi)
{
if (_M_pi != 0)
_M_pi->_M_weak_add_ref();
}
__weak_count<_Lp> 模板类有一个成员 _M_pi, 其类型为 _Sp_counted_base<_Lp>* _M_pi;
_Sp_counted_base<_Lp> 就包含上述的两个计数。use_count() 函数调用的是_M_refcount._M_get_use_count();
五,为什么要用 weak_ptr
1,解决shared_ptr 循环引用的问题。这个网上也有很多例子
2,在不延长shared_ptr 管理对象生命周期的情况下,探知这个对象是否已经无效。