Boost开发指南-3.6weak_ptr

news2024/11/14 13:58:06

weak_ptr

weak_ptr是为配合shared_ptr而引入的一种智能指针,它更像是shared_ptr的一个助手而不是智能指针,因为它不具有普通指针的行为,没有重载 operator*和->。它的最大作用在于协助shared_ptr工作,像旁观者那样观测资源的使用情况。

类摘要

template<class T>
class weak_ptr
{
public:
   weak_ptr(); //构造函数
   template<class Y> weak_ptr(shared_ptr<Y> const & r);
   weak_ptr(weak_ptr const & r);
   ~weak_ptr(); //析构函数
   weak_ptr &operator = (weak_ptr const & r); //赋值
   long use_count() const; //引用计数
   bool expired() const; //是否失效指针
   shared_ptr<T> lock() const; //获取shared_ptr
   void reset(); //重置指针
   void swap(weak_ptr<T> & b); //交换指针
};

weak_ptr的接口很小,正如它的名字,是一个“弱”指针,但它能够完成一些特殊的工作,足以证明它的存在价值。

用法

weak_ptr被设计为与shared_ptr协同工作,可以从一个shared_ptr或者另一个weak_ptr对象构造,获得资源的观测权。但weak_ptr没有共享资源,它的构造不会引起指针引用计数的增加。同样,weak_ptr析构时也不会导致引用计数减少,它只是一个静静的观察者。

使用weak_ptr的成员函数 use_count()可以观测资源的引用计数,另一个成员函数expired()的功能等价于use_count()==0,但更快,表示被观测的资源(也就是被shared_ptr管理的资源)已经不复存在。

weak_ptr没有重载operator*->,这是特意的,因为它不共享指针,不能操作资源,这正是它“弱”的原因。但它可以使用一个非常重要的成员函数lock()从被观测的shared_ptr获得一个可用的shared_ptr对象,把弱关系转换为强关系,从而操作资源。但当expired()==true的时候,lock()函数将返回一个存储空指针的shared_ptr。

shared_ptr<int> sp(new int(10)); //一个shared_ptr
assert(sp.use_count() == 1);

weak_ptr<int> wp(sp); //从shared_ptr创建weak_ptr
assert(wp.use_count() == 1); //weak_ptr不影响引用计数

if (!wp.expired()) //判断weak_ptr观察的对象是否失效
{
	shared_ptr<int> sp2 = wp.lock(); //获得一个shared_ptr
	*sp2 = 100;
	assert(wp.use_count() == 2); //退出作用域,sp2自动析构,引用计数减1
}

assert(wp.use_count() == 1);
sp.reset(); //shared_ptr失效
assert(wp.expired());
assert(!wp.lock()); //weak_ptr将获得一个空指针

enable_shared_from_this

weak_ptr的一个重要用途是获得this 指针的 shared_ptr,使对象自己能够生产shared_ptr管理自己:对象使用weak_ptr观测this指针,这并不影响引用计数,在需要的时候就调用lock()函数,返回一个符合要求的shared_ptr供外界使用。

这个解决方案被实现为一个惯用法,在头文件<boost/enable_shared_from_this.hpp>定义了一个助手类enable_shared_from_this<T>,它的声明摘要如下:

template<class T>
class enable_shared_from_this  //辅助类,需要继承使用
public:
   shared_ptr<T> shared_from_this(); //工厂函数,产生this的shared_ptr

使用的时候只需要让想被shared_ptr管理的类从它派生即可,成员函数shared_from_this()会返回this的shared_ptr。例如:

class self_shared: //一个需要用shared_ptr自我管理的类
   public enable_shared_from_this<self_shared>
{
public:
   self_shared(int n) : x(n){}
   int x;
   void print()
   { cout<< "self_shared : " << x << endl; }
};

int main()
{
     auto sp = make_shared<self_shared>(313);
     sp->print();
     auto p = sp->shared_from_this();
     p->x = 1000;
     p->print();
}

需要注意的是千万不能对一个普通对象(非shared_ptr管理的对象)使用shared_from_this()获取shared_ptr,例如:

self_shared ss;
auto p = ss.shared_from_this(); //错误

这样虽然语法上正确,编译也无问题,但在运行时会导致shared ptr析构时企图删除一个栈上分配的对象,发生未定义行为。

enable_shared_from_raw

smart_ptr库在未文档化的头文件<boost/smart_ptr/enable_shared_from_raw .hpp>里提供另外一个与 enable_shared_from_this类似的辅助类enable_shared_from_raw,它不要求对象必须被一个shared_ptr管理,可以直接从一个原始指针创建出shared_ptr。

enable_shared_from_raw的类摘要如下:

class enable_shared_from_raw
{
protected:
   enable_shared_from_raw();
   enable_shared_from_raw(enable_shared_from_raw const &);
   enable_shared_from_raw & operator=(enable_shared_from_raw const &);
   ~enable_shared_from_raw()
private :
   template<class Y> friend class shared_ptr;
   template<typenameT>
   friend boost::shared_ptr<T> shared_from_raw(T *);
   template<typename T>
   friend boost::weak_ptr<T> weak_from_raw(T *);
};

enable_shared_from_raw利用了shared_ptr的别名构造函数特性,内部持有一个void*的空指针shared_ptr作为引用计数的观察者,从而达到管理原始指针的目的。

enable_shared_from_raw同样需要继承使用,但它不是模板类,所以不需要指定模板参数,比 enable_shared_from_this写法上要简单一些。它不提供成员函数shared_from_this(),而是用两个friend函数shared_from_raw()和weak_from_raw()完成创建智能指针的工作。

但请注意:在调用shared_from_raw()后,由于存在shared_ptr成员变量的原因,对象内部会有一个shared_ptr的强引用,所以即使其他的 shared_ptr都析构了原始指针也不会被自动删除(因为use_count() >=1)——这使得enable_shared_from_raw用法略微不同于enable_shared_from_this,它可以安全地从一个普通对象而非指针创建出shared_ptr。

示范enable_shared_from_raw用法的代码如下:

#include <boost/smart_ptr/enable_shared_from_raw.hpp>

class raw_shared :
	public boost::enable_shared_from_raw
{
public:
	raw_shared()
	{
		std::cout << "raw_shared ctor" << std::endl;
	}
	~raw_shared()
	{
		std::cout << "raw_shared dtor" << std::endl;
	}
};

int main()
{
	raw_shared x; //一个普通对象
	assert(weak_from_raw(&x).use_count() == 1); //此时无引用,注意要用&取地址
	
	auto px = shared_from_raw(&x); //获取shared_ptr
	assert(px.use_count() == 2); //引用计数为2!
} //对象自动删除

把enable_shared_from_raw应用于原始指针要当心,使用不当有可能造成内存泄漏:

int main()
{
	auto p = new raw_shared; //创建一个原始指针

	auto wp = weak_from_raw(p); //获取weak_ptr
	assert(wp.use_count() == ); //此时无引用

	auto sp = shared_from_raw(p); //获取shared_ptr
	assert(sp.use_count() == 2); //引用计数为2!

	auto sp2 = sp; //拷贝一个shared_ptr
	auto wp2 = weak_from_raw(p); //获取weak_ptr
	assert(wp2.use_count() == 3); //引用计数为3
} //对象没有被删除,内存泄露!

如果在代码里的某个时刻使用shared_ptr来管理原始指针——而不是调用shared_from_raw(),那么指针的管理权就会转移到 shared_ptr,从而可以正确地自动销毁,例如:

int main()
{
	auto p = new raw_shared; //创建一个原始指针
    decltype(shared_from_raw(p)) spx(p); //使用shared_ptr管理指针
	... //其他操作
} //对象被自动删除

enable_shared_from_raw的用法比较特殊,实际应用的场景较少,也许这就是它没有在 Boost库里被文档化的原因。

打破循环引用

有的时候代码中可能会出现“循环引用”,这时shared_ptr的引用计数机制就会失效,导致不能正确释放资源,例如:

#include <boost/smart_ptr.hpp>
using namespace boost;

class node //一个用于链表节点的类
{
public:
	~node() //析构函数输出信息
	{
		std::cout << "deleted" << std::endl;
	}

	typedef shared_ptr<node> ptr_type; //指针类型使用shared_ptr
	ptr_type next; //后继指针
};

int main()
{
	auto p1 = make_shared<node>(); //两个节点对象
	auto p2 = make_shared<node>();

	p1->next = p2; //形成循环链表
	p2->next = p1; 

	assert(p1.use_count() == 2); //每个shared_ptr的引用计数都是2
	assert(p2.use_count() == 2);

} //退出作用域,shared_ptr无法正确析构

上面的代码中两个节点对象互相持有对方的引用,每一个shared_ptr的引用计数都是2,因此在析构时引用计数没有减至0,不会调用删除操作,导致内存泄漏。

这个时候我们就可以使用weak_ptr,因为它不会增加智能指针的引用计数,这样就把原来的强引用改变为弱引用,在可能存在循环引用的地方打破了循环,而在真正需要shared_ptr的时候调用weak_ptr的lock()函数:

class node //一个用于链表节点的类
{
public:
	~node() //析构函数输出信息
	{
		std::cout << "deleted" << std::endl;
	}

	typedef weak_ptr<node> ptr_type; //指针类型使用weak_ptr
	ptr_type next; //后继指针
};

int main()
{
	auto p1 = make_shared<node>(); //两个节点对象
	auto p2 = make_shared<node>();

	p1->next = p2; //形成循环链表
	p2->next = p1; //引用使用了weak_ptr所以正常

	assert(p1.use_count() == 1); //每个shared_ptr的引用计数都是2
	assert(p2.use_count() == 1); //没有了循环引用

    if (!p1->next.expired()) //检查弱引用是否有效
	{
		auto p3 = p1->next.lock(); //调用lock()获得强引用
	}

} //退出作用域,shared_ptr均正确析构

代码示例

#include <iostream>
//using namespace std;

#include <boost/smart_ptr.hpp>
using namespace boost;

//

void case1()
{
	shared_ptr<int> sp(new int(10));
	assert(sp.use_count() == 1);

	weak_ptr<int> wp(sp);
	assert(wp.use_count() == 1);
	assert(!wp.empty());

	if (!wp.expired())
	{
		shared_ptr<int> sp2 = wp.lock();
		*sp2 = 100;
		assert(wp.use_count() == 2);
	}

	assert(wp.use_count() == 1);
	sp.reset();
	assert(wp.expired());
	assert(!wp.lock());
}

//

class self_shared :
	public enable_shared_from_this<self_shared>
{
public:
	self_shared(int n) :x(n) {}
	int x;
	void print()
	{
		std::cout << "self_shared:" << x << std::endl;
	}
};

void case2()
{
	auto sp = make_shared<self_shared>(313);
	sp->print();

	auto p = sp->shared_from_this();

	p->x = 1000;
	p->print();
}

//

class node
{
public:
	~node()
	{
		std::cout << "deleted" << std::endl;
	}

	typedef weak_ptr<node> ptr_type;
	//typedef shared_ptr<node> ptr_type;
	ptr_type next;
};

void case3()
{
	auto p1 = make_shared<node>();
	auto p2 = make_shared<node>();

	p1->next = p2;
	p2->next = p1;

	assert(p1.use_count() == 1);
	assert(p2.use_count() == 1);

	if (!p1->next.expired())
	{
		auto p3 = p1->next.lock();
	}
}

//
#include <boost/smart_ptr/enable_shared_from_raw.hpp>

class raw_shared :
	public enable_shared_from_raw
{
public:
	raw_shared()
	{
		std::cout << "raw_shared ctor" << std::endl;
	}
	~raw_shared()
	{
		std::cout << "raw_shared dtor" << std::endl;
	}
};

void case4()
{
	raw_shared x;
	assert(weak_from_raw(&x).use_count() == 1);
	auto px = shared_from_raw(&x);
	assert(px.use_count() == 2);

	auto p = new raw_shared;

	auto wp = weak_from_raw(p);
	assert(wp.use_count() == 1);

	decltype(shared_from_raw(p)) spx(p);

	auto sp = shared_from_raw(p);
	//std::cout << sp.use_count() << std::endl;
	assert(sp.use_count() == 2);

	//decltype(sp) spx(p);

	auto sp2 = sp;
	auto wp2 = weak_from_raw(p);
	assert(wp2.use_count() == 3);
}

//

int main()
{
	case1();
	case2();
	case3();
	case4();
}

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/815663.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

伴伴加速“搭子社交”:确认过眼神,是一起玩游戏的人

最近在年轻人中风靡起了一种新型的社交方式——“搭子”社交。例如饭搭子、学习搭子、游戏搭子、减肥搭子......在搭子社交流行的当下&#xff0c;伴伴抓住了这一点&#xff0c;为年轻人提供了一个线上社交平台&#xff0c;让他们能够找到与自己兴趣相投的搭子。 什么是搭子社…

回归预测 | MATLAB实现SO-CNN-LSTM蛇群算法优化卷积长短期记忆神经网络多输入单输出回归预测

回归预测 | MATLAB实现SO-CNN-LSTM蛇群算法优化卷积长短期记忆神经网络多输入单输出回归预测 目录 回归预测 | MATLAB实现SO-CNN-LSTM蛇群算法优化卷积长短期记忆神经网络多输入单输出回归预测预测效果基本介绍模型描述程序设计参考资料 预测效果 基本介绍 MATLAB实现SO-CNN-LS…

接口自动化测试-Jmeter+ant+jenkins实战持续集成(详细)

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 1、下载安装配置J…

vue sku商品规格多选

vue sku商品规格多选 1.创建一个数据对象&#xff0c;用于存储SKU的选中状态。例如&#xff0c;可以使用一个数组来表示选中的SKU&#xff0c;每个元素代表一个SKU选项的id。 data() {return {selectedOptions: []} }2.在SKU选项列表中&#xff0c;使用v-bind:class绑定一个计…

医疗陪诊小程序开发:为您打造贴心、专业的陪护服务

近年来&#xff0c;随着人们对健康关注度的提高和医疗服务需求的增加&#xff0c;陪诊小程序逐渐崭露头角。陪诊小程序是一种基于互联网技术的应用程序&#xff0c;旨在提供便捷、高效的陪诊服务。下面将介绍陪诊小程序开发的优势。   便捷性&#xff1a;陪诊小程序可以通过手…

类和对象——初始化列表

目录 初始化列表 注意 单/多参数传入 explicit关键字 匿名函数 先前&#xff0c;我们知道有构造函数——用于给各成员变量一个初始值。 但是仍然不能称为是初始化&#xff0c;因为初始化只能初始化一次&#xff0c;但是构造函数里可以多次赋值 初始化列表 以日期类Date…

【Golang】解决Go test执行单个测试文件提示未定义问题

目录 背景 根本原因 解决方法 解决 多级引用或多个引用包的情况 总结 资料获取方法 背景 很多人记录过怎么执行Go test单个文件或者单个函数&#xff0c;但是要么对执行单文件用例存在函数或变量引用的场景避而不谈&#xff0c;要么提示调用了其它文件中的模块会报错。其…

Coremail中睿天下|2023年第二季度企业邮箱安全态势观察

7月24日&#xff0c;Coremail邮件安全联合中睿天下发布《2023第二季度企业邮箱安全性研究报告》&#xff0c;对2023第二季度和2023上半年的企业邮箱的安全风险进行了分析。 一、垃圾邮件同比下降16.38% 根据Coremail邮件安全人工智能实验室&#xff08;以下简称AI实验室&#…

c++(强生成关键字+可变参数模板+emplace)[26]

强制生成 不生成 在C中&#xff0c;可以通过一些方式来控制编译器是否生成某些特殊成员函数&#xff08;如默认构造函数、拷贝构造函数、拷贝赋值运算符、析构函数等&#xff09;。 默认生成&#xff1a;如果你没有显式地定义这些特殊成员函数&#xff0c;编译器会自动生成它们…

GNSS技术知识你知道多少?这些你或许还未掌握

GNSS信号频段 GNSS频谱图展示了不同的GNSS信号及其星座、载波频率、调制方案&#xff0c;以及所有这些信号在同一L波段频段内如何相互关联&#xff0c;是GNSS专业人员的必备工具&#xff0c;包括设计和开发GNSS系统的工程师&#xff0c;以及测试GNSS系统的工程师。 GNSS术语 …

如何清理电脑缓存垃圾

1、手动清理电脑缓存 C:\Users\yangguang\AppData\Local\Temp 注&#xff1a;yangguang 是自己电脑对应的用户目录 2、清理完后&#xff0c;电脑流畅了许多。

C++继承(2)——赋值转换、隐藏特性以及作用域

目录 一.子类和父类对象的赋值转换 子类对象赋值父类对象的另外两种方式&#xff1a; 总结&#xff1a; 二.父类与子类的作用域 1. 在继承体系中基类和派生类都有独立的作用域。 例&#xff1a; 2.作用域练习 练习1&#xff1a; 解决方法: 一.子类和父类对象的赋值转换 …

Reinforcement Learning with Code 【Code 2. Tabular Sarsa】

Reinforcement Learning with Code 【Code 2. Tabular Sarsa】 This note records how the author begin to learn RL. Both theoretical understanding and code practice are presented. Many material are referenced such as ZhaoShiyu’s Mathematical Foundation of Rei…

【C# 6.0】云LIS平台源码

基于云计算的区域LIS平台为医疗机构改善患者服务质量提供了强有力的支持&#xff0c;“以患者为核心”这一理念得到了充分实现&#xff0c;可以解决各医院LIS建设水平参差不齐的现状&#xff0c;并完善各医院内LIS系统的功能&#xff0c;实现数据标准统一、功能完善、性能可靠&…

VR全景旅游,智慧文旅发展新趋势!

引言&#xff1a; VR全景旅游正在带领我们踏上一场全新的旅行体验。这种沉浸式的旅行方式&#xff0c;让我们可以足不出户&#xff0c;却又身临其境地感受世界各地的美景。 一&#xff0e;VR全景旅游是什么&#xff1f; VR全景旅游是一种借助虚拟现实技术&#xff0c;让用户…

go env 配置(环境变量)说明

前提&#xff1a;已经安装好 golang 可正确的运行下面这段命令&#xff0c;来查看 go 的配置&#xff1a; go env 输出示例&#xff1a; 以上是我本地(windows)环境下输出的配置信息(环境变量) 我们这次就针对每个配置信息进行一个说明&#xff0c;具体到每个字段是什么意思…

浅谈Vue3 computed计算属性

什么是computed 官方给出的解释&#xff1a;接受一个 getter 函数&#xff0c;返回一个只读的响应式 ref 对象。该 ref 通过 .value 暴露 getter 函数的返回值。它也可以接受一个带有 get 和 set 函数的对象来创建一个可写的 ref 对象 // 只读 function computed<T>(ge…

远程控制软件安全吗?一文看懂ToDesk、RayLink、TeamViewer、Splashtop相关安全机制

目录 一、前言 二、远程控制中的安全威胁 三、国内外远控软件安全机制 【ToDesk】 【RayLink】 【Teamviewer】 【Splashtop】 四、安全远控预防 一、前言 近期&#xff0c;远程控制话题再一次引起关注。 据相关新闻报道&#xff0c;不少不法分子利用远程控制软件实施网络诈骗&…

直播预告 | 开源运维工具使用现状以及可持续产品的思考

运维平台自上世纪90年代开始进入中国市场&#xff0c;曾形成以传统四大外企&#xff1a;IBM、BMC、CA、HP为代表的头部厂商&#xff0c;还有一众从网管起家的国内厂商。2010年前后&#xff0c;出现了以Zabbix、Nagios、Cacti为代表的开源工具&#xff0c;后来又陆续出现了Prome…

如何计算文本的困惑度perplexity(ppl)

前言 本文关注在Pytorch中如何计算困惑度&#xff08;ppl&#xff09;为什么能用模型 loss 代表 ppl 如何计算 当给定一个分词后的序列 X ( x 0 , x 1 , … , x t ) X (x_0, x_1, \dots,x_t) X(x0​,x1​,…,xt​), ppl 计算公式为&#xff1a; 其中 p θ ( x i ∣ x &l…