一. 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>();
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 ;
}

二. 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) = ;
};
//机场:具体观察者
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 oPtr)
{
if (obs.find(oPtr) == obs.end()) {
obs.insert(oPtr);
}
}
//注销观察者
void unregisterObserver(const ObserverPtr oPtr) //oPtr为weak_ptr类型
{
if (obs.find(oPtr) != obs.end())
{
obs.erase(oPtr);
}
} //通知各个观察者
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(); station.unregisterObserver(school);
station.notifyObservers(); return ;
}
/*输出结果
Airport: 1
School: 1
Airport: 2
*/

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

  1. 第21课 shared_ptr共享型智能指针

    一. shared_ptr的基本用法 (一)与unique_ptr的比较 比较 shared_ptr unique_ptr 备注 初始化 ①shared_ptr<T> sp; sp.res ...

  2. stl中auto_ptr,unique_ptr,shared_ptr,weak_ptr四种智能指针使用总结

    stl中auto_ptr,unique_ptr,shared_ptr,weak_ptr四种智能指针使用总结 1. auto_ptrauto_ptr主要是用来解决资源自动释放的问题,比如如下代码:voi ...

  3. 第20课 unique_ptr独占型智能指针

    一. unique_ptr的基本用法 (一)初始化方式 1. 直接初始化:unique<T> myPtr(new T);  //ok.但不能通过隐式转换来构造,如unique<T&g ...

  4. C11内存管理之道:智能指针

    1.shared_ptr共享智能指针 std::shared_ptr使用引用计数,每个shared_ptr的拷贝都指向相同的内存,在最后一个shared_ptr析构的时候,内存才会释放. 1.1 基本 ...

  5. 详解 boost 库智能指针(scoped_ptr<T> 、shared_ptr<T> 、weak_ptr<T> 源码分析)

    一.boost 智能指针 智能指针是利用RAII(Resource Acquisition Is Initialization:资源获取即初始化)来管理资源.关于RAII的讨论可以参考前面的文章.在使 ...

  6. weak_ptr<T>智能指针

    weak_ptr是为配合shared_ptr而引入的一种智能指针,它更像是shared_ptr的一个助手,而不是智能指针,因为它不具有普通指针的行为,没有重载operator*和operator-&g ...

  7. 智能指针 auto_ptr、scoped_ptr、shared_ptr、weak_ptr

    什么是RAII? RAII是Resource Acquisition Is Initialization的简称,是C++语言的一种管理资源.避免泄漏的惯用法. RAII又叫做资源分配即初始化,即:定义 ...

  8. [6] 智能指针boost::weak_ptr

    [1]boost::weak_ptr简介 boost::weak_ptr属于boost库,定义在namespace boost中,包含头文件 #include<boost/weak_ptr.hp ...

  9. 智能指针之 weak_ptr

    1. weak_ptr 介绍 std::weak_ptr 是一种智能指针,它对被 std::shared_ptr 管理的对象存在非拥有性("弱")引用.在访问所引用的对象指针前必须 ...

随机推荐

  1. 【09】Nginx:静态压缩 / 日志切割 / 防盗链 /恶意解析/ 跨域

    写在前面的话 上一节我们谈了关于 nginx 服务器的一些简单的安全优化问题,能够帮助我们解决一部分线上服务存在的安全隐患.但是想要提升用户体验这是原因不够的,我们还需要从服务的优化方面入手. 本节更 ...

  2. 转 Yolov3转化Caffe框架详解

    转自https://blog.csdn.net/watermelon1123/article/details/82083522 前些日子因工程需求,需要将yolov3从基于darknet转化为基于Ca ...

  3. 你不知道的Golang盲点汇总【持续更新】

    1. 引用传递时append会复制生成新的指针 package main ] ) //append后s指定新复制的内存指针了,不再指向原来的内存 s=append(s, ) fmt.Printf(] ...

  4. C 结构体、位域

    参考链接:https://www.runoob.com/cprogramming/c-structures.html 结构体是干啥的 例如数组可以用来存储多个相同数据类型的数据项,结构体也是一种数据类 ...

  5. 花了近十年的时间,整理出史上最全面Java面试题

    1.String 是最基本的数据类型吗? 不是.Java中的基本数据类型只有8个:byte.short.int.long.float.double.char.boolean:除了基本类型(primit ...

  6. Flask笔记:cookie

    在网站中,HTTP请求是无状态的:第一次请求成功后,第二次请求时服务器依然不知道这次请求的所属用户是谁.为了解决这个问题,在第一次请求成功后,服务器会生成并返回对应的cookie信息给浏览器,而浏览器 ...

  7. jenkins+robotframework邮件发送报告模板

    1.Jenkins中配置系统邮件系统管理–系统设置,配置Extended E-mail Notification 2.jenkins 创建一个新项目,项目创建成功,配置邮件

  8. php中函数 isset(), empty(), is_null() 的区别,boolean类型和string类型的false判断

    php中函数 isset(), empty(), is_null() 的区别,boolean类型和string类型的false判断 实际需求:把sphinx返回的结果放到ssdb缓存里,要考虑到sph ...

  9. sparkSQL中的example学习(2)

    UserDefinedUntypedAggregate.scala(默认返回类型为空,不能更改) import org.apache.spark.sql.{Row, SparkSession} imp ...

  10. python实现LRU热点缓存

    基于列表+Hash的LRU算法实现. 访问某个热点时,先将其从原来的位置删除,再将其插入列表的表头 为使读取及删除操作的时间复杂度为O(1),使用hash存储热点的信息的键值 class LRUCac ...