C++11 Weak_Ptr 源码分析

所谓智能指针,可以从字面上理解为“智能”的指针。具体来讲,智能指针和普通指针的用法是相似的,不同之处在于,智能指针可以在适当时机自动释放分配的内存。也就是说,使用智能指针可以很好地避免“忘记释放内存而导致内存泄漏”问题出现。由此可见,C++ 也逐渐开始支持垃圾回收机制了,尽管目前支持程度还有限。

c++11 中发布了C 11 Shared_Ptr 与 Make_Shared源码剖析详解C 11 Unique_Ptr 与 Make_Unique源码剖析C 11 Weak_Ptr 源码分析用以资源的管理,都是定义在memory 这个头文件中。

  • std::shared_ptr 允许多个shared_ptr 实例指向同一个对象,通过计数管理;
  • std::unique_ptr 是独占对象;
  • weak_ptr 是辅助类,是一种弱引用,指向shared_ptr 所管理的对象。
1. 源码分析
1.1 头文件
#include <memory>
1.2 构造
constexpr weak_ptr() noexcept;
    template<class Y> weak_ptr(shared_ptr<Y> const& r) noexcept;
    weak_ptr(weak_ptr const& r) noexcept;
    template<class Y> weak_ptr(weak_ptr<Y> const& r) noexcept;
    weak_ptr(weak_ptr&& r) noexcept;                      // C++14
    template<class Y> weak_ptr(weak_ptr<Y>&& r) noexcept; // C++14

构造函数相比shared_ptr 和unique_ptr 少了很多,其中移动构造函数是c++14 发布的。

另外,weak_ptr 中的对象只能通过shared_ptr 或 另一个weak_ptr 实例获取。

1.2.1 weak_ptr 的移动构造函数
template<class _Tp>
inline
weak_ptr<_Tp>::weak_ptr(weak_ptr&& __r) _NOEXCEPT
    : __ptr_(__r.__ptr_),
      __cntrl_(__r.__cntrl_)
{
    __r.__ptr_ = 0;
    __r.__cntrl_ = 0;
}

同shared_ptr,weak_ptr 中存放一个对象的指针__ptr_和用以计数的__cntrl_。

注意的是__cntrl_ 虽然是可以从shared_ptr 中获取(构造参数为shared_ptr时),但是weak_ptr 并不会增加对象指针的shared 引用计数,而只是增加weak 计数。

1.2.2  weak_ptr 的拷贝构造函数
template<class _Tp>
inline
weak_ptr<_Tp>::weak_ptr(weak_ptr const& __r) _NOEXCEPT
    : __ptr_(__r.__ptr_),
      __cntrl_(__r.__cntrl_)
{
    if (__cntrl_)
        __cntrl_->__add_weak();
}

会通过__cntrl_ 调用add_weak,而不是add_shared。

下面举例shared_ptr 通常的创建方式:

std::weak_ptr<int> wp1;                  //不传入任何实参

std::shared_ptr<int> sp (new int);
std::weak_ptr<int> wp3 (sp);             //没有直接传对象指针的构造,只能通过shared_ptr 或者weak_ptr
1.3 赋值重载
weak_ptr& operator=(weak_ptr const& r) noexcept;
    template<class Y> weak_ptr& operator=(weak_ptr<Y> const& r) noexcept;
    template<class Y> weak_ptr& operator=(shared_ptr<Y> const& r) noexcept;
    weak_ptr& operator=(weak_ptr&& r) noexcept;                      // C++14
    template<class Y> weak_ptr& operator=(weak_ptr<Y>&& r) noexcept; // C++14
1.4 修改的接口
void swap(weak_ptr& r) noexcept;
    void reset() noexcept;
1.5 获取
long use_count() const noexcept;
    bool expired() const noexcept;
    shared_ptr<T> lock() const noexcept;
    template<class U> bool owner_before(shared_ptr<U> const& b) const noexcept;
    template<class U> bool owner_before(weak_ptr<U> const& b) const noexcept;

对于weak_ptr 的成员函数总结如下:

成员方法

功 能

operator=()

重载 = 赋值运算符,是的 weak_ptr 指针可以直接被 weak_ptr 或者 shared_ptr 类型指针赋值。

swap(x)

其中 x 表示一个同类型的 weak_ptr 类型指针,该函数可以互换 2 个同类型 weak_ptr 指针的内容。

reset()

将当前 weak_ptr 指针置为空指针。

use_count()

查看指向和当前 weak_ptr 指针相同的 shared_ptr 指针的数量。

expired()

判断当前 weak_ptr 指针为否过期(指针为空,或者指向的堆内存已经被释放)。

lock()

如果当前 weak_ptr 已经过期,则该函数会返回一个空的 shared_ptr 指针;反之,该函数返回一个和当前 weak_ptr 指向相同的 shared_ptr 指针。

注意:

  •  weak_ptr被设计为与shared_ptr共同工作,可以从一个shared_ptr或者另一个weak_ptr对象构造,获得资源的观测权。但weak_ptr没有共享资源,它的构造不会引起指针引用计数的增加。同样,在weak_ptr析构时也不会导致引用计数的减少,它只是一个静静地观察者。weak_ptr没有重载operator*和->,这是特意的,因为它不共享指针,不能操作资源,这是它弱的原因。但它可以使用一个非常重要的成员函数lock()从被观测的shared_ptr获得一个可用的shared_ptr对象,从而操作资源;
  •  weak_ptr用于解决”引用计数”模型循环依赖问题,weak_ptr指向一个对象,并不增减该对象的引用计数器。weak_ptr用于配合shared_ptr使用,并不影响动态对象的生命周期,即其存在与否并不影响对象的引用计数器。weak_ptr并没有重载operator->和operator *操作符,因此不可直接通过weak_ptr使用对象。weak_ptr提供了expired()与lock()成员函数,前者用于判断weak_ptr指向的对象是否已被销毁,后者返回其所指对象的shared_ptr智能指针(对象销毁时返回”空”shared_ptr);
  • 智能指针是模板类而不是指针;
  • weak_ptr并没有重载operator->和operator *操作符,因此不可直接通过weak_ptr使用对象,典型的用法是调用其lock函数来获得shared_ptr示例进而访问原始对象
  •  weak_ptr是一种不控制所指向对象生存期的智能指针,它指向由一个shard_ptr管理的对象。将一个weak_ptr绑定到一个shared_ptr不会改变shared_ptr的引用计数。一旦最后一个指向对象的shared_ptr被销毁,对象就会被释放。即使有weak_ptr指向对象,对象也还是会被释放;
  •  当创建一个weak_ptr时,要用一个shared_ptr来初始化它。不能使用weak_ptr直接访问对象,而必须调用lock。此函数检查weak_ptr指向的对象是否仍存在。如果存在,lock返回一个指向共享对象的shared_ptr。与任何其它shared_ptr类似,只要此shared_ptr存在,它所指向的底层对象也就会一直存在;
2. 举例
#include <iostream>
#include <memory>
 
std::weak_ptr<int> gw;
 
void observe()
{
    std::cout << "use_count == " << gw.use_count() << ": ";
    if (auto spt = gw.lock()) { // Has to be copied into a shared_ptr before usage
	    std::cout << *spt << "\n";
    }
    else {
        std::cout << "gw is expired\n";
    }
}
 
int main()
{
    {
        auto sp = std::make_shared<int>(42);
	    gw = sp;
 
	    observe();
    }
 
    observe();
}

运行结果:

use_count == 1: 42
use_count == 0: gw is expired

在区域执行完成后,对象会被释放,虽然gw 还在,但是通过lock 获取的shared_ptr 已经为空

再来看个例子:

#include <iostream>
#include <memory>
using namespace std;

int main()
{
    std::shared_ptr<int> sp1(new int(10));
    std::shared_ptr<int> sp2(sp1);
    std::weak_ptr<int> wp(sp2);
    //输出和 wp 同指向的 shared_ptr 类型指针的数量
    cout << wp.use_count() << endl;
    //释放 sp2
    sp2.reset();
    cout << wp.use_count() << endl;
    //借助 lock() 函数,返回一个和 wp 同指向的 shared_ptr 类型指针,获取其存储的数据
    cout << *(wp.lock()) << endl;
    return 0;
}

运行结果:

2
1
10
收藏 (0)
评论列表
正在载入评论列表...
我是有底线的
为您推荐
    暂时没有数据