第22课 weak_ptr弱引用智能指针

一. weak_ptr的概况

(一)weak_ptr的创建

  1. 直接初始化:weak_ptr<T> wp(sp); //其中sp为shared_ptr类型

  2. 赋值: wp1 = sp; //其中sp为shared_ptr类型

      wp2 = wp1; //其中wp1为weak_ptr类型

(二)常用操作

  1. use_count():获取当前控制块中资源的强引用计数。

  2. expired():判断所观测的资源是否失效(即己经被释放),即use_count是否为0。

  (1)shared_ptr<int> sp1 = wp.lock();//如果wp失效,则sp为空(其中wp为weak_ptr类型)

  (2)shared_ptr<int> sp2(wp); //如果wp失效,则抛std::bad_weak_ptr异常

  3. lock():获取所监视资源的shared_ptr,如shared_ptr<int> sp = wp.lock(); //wp为weak_ptr类型。

  4. reset():重置weak_ptr,影响弱引用计数

(三)注意事项

  1. weak_ptr不是独立的智能指针,它是shared_ptr的助手,只是监视shared_ptr管理的资源是否释放,不会影响强引用计数,不能管理资源。

  2.weak_ptr没有重载操作符*和->,因为它不共享指针,不能操作资源。

  3.weak_ptr主要用来代替可能空悬的shared_ptr

【编程实验】weak_ptr初体验

#include <iostream>
#include <memory>

using namespace std;

int main()
{
    auto sp1 = make_shared<int>(10);
    weak_ptr<int> wp(sp1);  //通过shared_ptr初始化
    weak_ptr<int> wp1, wp2;
    wp1 = sp1;   //利用shared_ptr来赋值
    wp2 = wp;    //利用weak_ptr赋值
    auto sp2 = wp2.lock(); //sp2为shared_ptr类型

    sp1 = nullptr;

    cout << wp2.use_count() << endl; //1,强引用计数
    return 0;
}

二. weak_ptr的应用

(一)缓存对象

  1. 考虑一个工厂函数loadWidget,该函数基于唯一ID来创建一些指向只读对象的智能指针。

  2. 假设该只读对象需要被频繁使用,而且经常需要从文件或数据库中加载。那么可以考虑将对象缓存起来。同时为了避免过量缓存,当不用使用时,则将该对象删除。

  3. 由于带缓存,因此工厂函数返回unique_ptr类型显然不合适。这里因为调用者和缓存管理器均需要一个指向这些对象的指针。

  4. 当用户用完工厂函数返回的对象后,该对象会被析构,此时相应的缓存条目将会空悬。因为可以考虑将工厂函数的返回值设定为shared_ptr类型,而缓存类型为weak_ptr类型

(二)观察者模式

1. 观察者模式是在subject状态发生改变时,通知观察者的一种设计模式。

2. 在多数实现中,每个subject持有指向观察者的指针,这使得当subject状态改变时可以很容易通知观察者。

3. subject不会控制其观察者的生存期,因此应该是持有观察者的weak_ptr指针。同时在subject的使用某个指针时,可以先确定是否空悬。

(三)解决循环引用

  1. A、B、C三个对象的数据结构中,A和C共享B的所有权,因此各持有一个指向B的std::shared_ptr;

  2. 假设有一个指针从B指回A(即上图中的红色箭头),则该指针的类型应为weak_ptr,而不能是裸指针或shared_ptr,原因如下:

   ①假如是裸指针,当A被析构时,由于C仍指向B,所以B会被保留。但B中保存着指向A的空悬指针,而B却检测不出来,但解引用该指针时会产生未定义行为。

   ②假如是shared_ptr时。由于A和B相互保存着指向对方的shared_ptr,此时会形成循环引用,从而阻止了A和B的析构。

   ③假如是weak_ptr,这可以避免循环引用。假设A被析构,那么B的回指指针会空悬,但B可以检测到这一点,同时由于该指针是weak_ptr,不会影响A的强引用计数,因此当shared_ptr不再指向A时,不会阻止A的析构。

(四)监视this智能指针:见《第21课》中的enable_shared_from_this,其中的weak_this_指针即为weak_ptr类型,用于监视this指针。

【编程实验】weak_ptr的使用

#include <iostream>
#include <memory> //for smart pointer
#include <unordered_map> //for unordered_map
#include <set>

using namespace std;

class Widget
{
public:
    Widget(int id):ID(id){}

    int ID;
};

//1. 利用weak_ptr来缓存对象
//模拟从数据库中加载,并创建shared_ptr指向widget对象
shared_ptr<Widget> loadWidget(int WidgetID)
{
    return make_shared<Widget>(WidgetID);
}

//带缓存的工厂函数
std::shared_ptr<const Widget> fastloadWidget(int WidgetID) //返回值为shared_ptr类型
{
    //缓存:weak_ptr类型
    static std::unordered_map<int, std::weak_ptr<const Widget>> cache;

    auto objPtr = cache[WidgetID].lock(); //objPtr的类型为shared_ptr,指向缓存的对象

    if (!objPtr) { //如果对象不在缓存中. 这里省略了缓存中因失效而不断累积std::weak_ptr的处理。
        objPtr = loadWidget(WidgetID);
        cache[WidgetID] = objPtr;
    }

    return objPtr;
}

//2. 观察者模式
//2.1 观察者
class WeatherObservers //抽象观察者
{
public:
    virtual void updateWeatherInfo(int num) = 0;
};
//机场:具体观察者
class Airport : public WeatherObservers
{
public:
    void updateWeatherInfo(int num) override
    {
        std::cout <<"Airport: " << num << endl;
    }
};
//学校:具体观察者
class School : public WeatherObservers
{
public:
    void updateWeatherInfo(int num) override
    {
        std::cout << "School: " << num << endl;
    }
};

//2.1 主题(气象站)
class WeatherStation
{
    using ObserverPtr = std::weak_ptr<WeatherObservers>; //弱引用

    //set集合中保存观察者的弱引用(以ObserverPtr为关键字,基于ownership排序)
    using ObserverList = std::set<ObserverPtr, std::owner_less<ObserverPtr>>;

    ObserverList obs; //保存所有观察者
public:
    //注册观察者
    void registerObserver(const ObserverPtr obsPtr)
    {
        if (obs.find(obsPtr) == obs.end()) {
            obs.insert(obsPtr);
        }
    }
    //注销观察者
    void unregisterObserver(const ObserverPtr obsPtr) //obsPtr为weak_ptr类型
    {
        if (obs.find(obsPtr) != obs.end())
        {
            obs.erase(obsPtr);
        }
    }

    //通知各个观察者
    void notifyObservers(int num)
    {
        std::shared_ptr<WeatherObservers> tempPtr;
        for (auto& ob : obs)
        {
            if ((tempPtr = ob.lock())) {
                tempPtr->updateWeatherInfo(num);
            }
        }
    }
};

int main()
{
    //观察者模式
    WeatherStation station;
    std::shared_ptr<Airport> airport(new Airport());
    std::shared_ptr<School>  school(new School());

    station.registerObserver(airport);
    station.registerObserver(school);

    station.notifyObservers(1);

    station.unregisterObserver(school);
    station.notifyObservers(2);

    return 0;
}
/*输出结果
Airport: 1
School: 1
Airport: 2
*/

原文地址:https://www.cnblogs.com/5iedu/p/11623757.html

时间: 2024-10-18 07:08:02

第22课 weak_ptr弱引用智能指针的相关文章

第21课 shared_ptr共享型智能指针

一. shared_ptr的基本用法 (一)与unique_ptr的比较 比较 shared_ptr unique_ptr 备注 初始化 ①shared_ptr<T> sp; sp.reset(new T()); ②shared_ptr<T> sp(new T()); ③shared_ptr<T> sp1 = sp; //拷贝构造 ④auto sp = make_shared<int>(10); ①unique_ptr<T> up; up.res

循环引用-智能指针的死穴之一

智能指针的实现思路也体现了C++基于对象的原则,对象应该为自己管理的资源负责,包括资源的分配与释放,而且最好将资源的释放与分配搞的自动化一点,典型的实现方法就是在构造函数里分配资源,在析构函数里释放资源,这样当其他程序员在使用这个对象时,该对象的资源问题几乎不用额外的操心,即优雅又方便 然后如此完美的东西,也有其不容忽视的地方,直接上代码: // share_ptr.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" #include "c

智能指针的模拟实现shared_ptr 循环引用 定置删除器

auto_ptr与scoped_ptr的实现见本人的上篇博客. 三.shared_ptr shared_ptr的实现原理是通过引用计数来实现,只有当引用计数为1时才释放空间,否则只需将引用计数减1.拷贝和赋值将引用计数加1,具体代码如下: template <typename T> class SharedPtr { public: SharedPtr(); SharedPtr(T* ptr); SharedPtr(const SharedPtr<T>& ap); ~Sha

C++ Primer 学习笔记_56_STL剖析(十一)(原boost库):详解智能指针(unique_ptr(原scoped_ptr) 、shared_ptr 、weak_ptr源码分析)

注意:现在boot库已经归入STL库,用法基本上还和boost类似 在C++11中,引入了智能指针.主要有:unique_ptr, shared_ptr, weak_ptr. 这3种指针组件就是采用了boost里的智能指针方案.很多有用过boost智能指针的朋友,很容易地就能发现它们之间的关间: std boost 功能说明 unique_ptr scoped_ptr 独占指针对象,并保证指针所指对象生命周期与其一致 shared_ptr shared_ptr 可共享指针对象,可以赋值给shar

C++ 资源管理(RAII)--智能指针

1. 智能指针(Smart Pointer) i. 是存储指向动态分配(堆)对象指针的类 ii. 在面对异常的时候格外有用,因为他们能够确保正确的销毁动态分配的对象 iii. RAII类模拟智能指针,见备注 2. C++11提供了以下几种智能指针,位于头文件<memory>,它们都是模板类 i. std::auto_ptr(复制/赋值) ii. std::unique_ptr  c++11 iii.std::shared_ptr  c++11 iv.std::weak_ptr    c++11

c++智能指针的不断演化

RAII RAII资源分配即初始化,定义一个类来封装资源的分配和释放,在构造 函数完成资源的分配和初始化,在析构函数完成资源的清理,可以保证资源的正确初始化和释放. 智能指针的引入: 由于return ,throw等关键字的存在,导致顺序执行流的错乱,不断的进行跳转,使开辟的空间 看似被释放,而实际上导致内存的泄露. 例如以下两个例子: void Test1() { int *p1 = new int(1); if (1) { return; } delete p1; } void DoSome

【C++】智能指针shared_ptr 定位删除器(仿函数)

智能指针shared_ptr 用引用计数实现看起来不错,但却存在问题. 1.引用计数更新存在着线程安全: 2.循环引用--使用一个弱引用智能指针(weak_ptr)来打破循环引用(weak_ptr不增加引用计数) 3.定置删除器和空间分配器 比如打开一个文件的关闭,用malloc开辟出来的空间,用引用计数实现会出现问题. 对改变引用计数时加锁可以解决引用计数更新存在着线程安全. 循环引用问题 #include<iostream> using namespace std; #include<

智能指针里弱引用应该如何实现?(WeakReference/WeakPtr&lt;T&gt;)

弱引用(指针)指的是这样一个概念:引用不控制对象的生命周期,但是对象本身销毁后,弱引用能得到"通知". WeakPtr的实现思路(概念上的):(WeakPtr 1)===> (引用结构 2) <==> (实际的对象 3) 当(实际的对象3)由于调用析构函数销毁自己时,可以清除(引用结构 2)里面的信息,这样(WeakPtr 1)就得到了null,相当于被通知到了 一种可能的实现:让WeakPtr作为一个全局WeakHashMap中的item成员,每当一个对象T创建时,

boost智能指针之shared_ptr和weak_ptr

std::auto_ptr很多的时候并不能满足我们的要求,比如auto_ptr不能用作STL容器的元素.boost的smart_ptr中提供了4种智能指针和2种智能指针数组来作为std::auto_ptr的补充. shared_ptr<boost/shared_ptr.hpp>:使用shared_ptr进行对象的生存期自动管理,使得分享资源所有权变得有效且安全. weak_ptr<boost/weak_ptr.hpp>:weak_ptr 是 shared_ptr 的观察员.它不会干