智能指针

news2024/9/24 13:22:21

目录

RAII

auto_ptr

unique_ptr

shared_ptr

shared_ptr的循环引用

weak_ptr

删除器


智能指针的出现主要是针对程序的资源泄露问题而产生的。

RAII

RAII(Resource Acquisition Is Initialization)是种利用对象生命周期来控制程序资源的简单技术。

在对象构造时获取资源,接着控制对资源的访问使之在对象的生命周期内始终保持有效,最后在对象析构的时候释放资源。借此,我们实际上把管理一份资源的责任托管给了一个对象。这种做法有两大好处: 1、不需要显式地释放资源。 2、采用这种方式,对象所需的资源在其生命期内始终保持有效。

智能指针还应具有指针的行为,因此还需要重载*和->

template<class T>
class SmartPtr{
public:
	SmartPtr(T* ptr)
		:_ptr(ptr)
	{}
	~SmartPtr(){
		cout << "delete:" << _ptr << endl;
		delete _ptr;
	}
	T& operator*(){
		return *_ptr;
	}
	T* operator->(){
		return _ptr;
	}
private:
	T* _ptr;
};

总结只能指针的原理:1、具有RAII特性2、重载operation*和operation->,具有指针一样的行为

auto_ptr

C++98版本的库中就提供了auto_ptr的智能指针。auto_ptr的实现原理:管理权转移的思想。下边是对auto_ptr的模拟

template<class T>
	class auto_ptr {
	public:
		auto_ptr(T* ptr)
			:_ptr(ptr)
		{}
		auto_ptr( auto_ptr<T>& ap):_ptr(ap._ptr) {
			ap._ptr = nullptr;
		}
		auto_ptr<T>& operator=(auto_ptr<T>& ap) {
			if (this != &ap) {
				if (_ptr) {
					delete _ptr;
				}
				_ptr = ap._ptr;
				ap._ptr = nullptr;
			}
			return *this;
		}
		~auto_ptr() {
			cout << "delete:" << _ptr << endl;
			delete _ptr;
		}
		T& operator*() {
			return *_ptr;
		}
		T* operator->() {
			return _ptr;
		}
	private:
		T* _ptr;
	};

 上边的ap1中的资源经过拷贝构造后,ap1的资源转移到了ap2去管理了。

unique_ptr

unique_ptr的实现原理:简单粗暴的防拷贝。下边是对unique_ptr的模拟

template<class T>
	class unique_ptr {
	public:
		unique_ptr(T* ptr)
			:_ptr(ptr)
		{}
		//防拷贝 C++11   
		unique_ptr(unique_ptr<T>& ap) = delete;
		unique_ptr<T>& operator=(unique_ptr<T>& ap) = delete;

		~unique_ptr() {
			cout << "delete:" << _ptr << endl;
			delete _ptr;
		}
		T& operator*() {
			return *_ptr;
		}
		T* operator->() {
			return _ptr;
		}
	private:
		T* _ptr;
	};

将拷贝构造和赋值重载进行了禁用。

shared_ptr

shared_ptr的原理:是通过引用计数的方式来实现多个shared_ptr对象之间共享资源。下边是对shared_ptr的模拟

template<class T>
	struct Delete {
		void operator()(T* ptr) {
			cout << "delete" << endl;
			delete ptr;
		}
	};
	template<class T, class D = Delete<T>>
	class shared_ptr{
	public:
		shared_ptr(T* ptr = nullptr)
			: _ptr(ptr)
			, _pCount(new int(1))
		{}

		void Release(){
			if (--(*_pCount) == 0){
				//cout << "Delete:" << _ptr << endl;
				//delete _ptr;

				D()(_ptr);
				delete _pCount;
			}
		}

		~shared_ptr(){
			Release();
		}

		// sp1(sp2)
		shared_ptr(const shared_ptr<T>& sp)
			: _ptr(sp._ptr)
			, _pCount(sp._pCount){
			(*_pCount)++;
		}

		// sp1 = sp5
		// sp1 = sp1
		shared_ptr<T>& operator=(const shared_ptr<T>& sp){
			//if (this == &sp)
			if (_ptr == sp._ptr){
				return *this;
			}

			// 减减被赋值对象的计数,如果是最后一个对象,要释放资源
			Release();

			// 共管新资源,++计数
			_ptr = sp._ptr;
			_pCount = sp._pCount;

			(*_pCount)++;

			return *this;
		}

		T& operator*(){
			return *_ptr;
		}
		T* operator->(){
			return _ptr;
		}
		int use_count() {
			return *_pCount;
		}
		T* get() const{
			return _ptr;
		}
	private:
		T* _ptr;
		int* _pCount;// 引用计数
	};

 我们可以观察到sp1的资源由2个对象(sp1、sp2)共享。sp3的资源仅由自己管理。sp4的资源由2个对象(sp4、sp5)共享。

shared_ptr的循环引用

struct Node{
	int _val;
	hzp::shared_ptr<Node> _next;
	hzp::shared_ptr<Node> _prev;

	~Node(){
		cout << "~Node" << endl;
	}
};

void test_weak_ptr(){
	std::shared_ptr<Node> n1(new Node);
	std::shared_ptr<Node> n2(new Node);

	n1->_next = n2;
	n2->_prev = n1;
}

上边的代码逻辑可以参考下图

待函数结束时,n2先析构,n1再析构,引用计数各自减至1。但是函数结束了,按理应该销毁,引用计数应该为0。但是左边中的_next指向的右边,右边的_prev指向的左边。二者谁也不让谁,就导致了循环引用的问题。 

weak_ptr

shared_ptr的循环引用问题的解决引出了weak_ptr。weak_ptr和特点是不增加引用计数,是一个辅助型的智能指针。

_next和_prev是weak_ptr的时候,其不参与资源释放管理,可以访问和修改资源,但是不增加计数,就不存在循环引用问题了。

下边是weak_ptr的模拟

// 辅助型智能指针,使命配合解决shared_ptr循环引用问题
	template<class T>
	class weak_ptr{
	public:
		weak_ptr()
			:_ptr(nullptr)
		{}

		weak_ptr(const shared_ptr<T>& sp)
			:_ptr(sp.get())
		{}

		weak_ptr(const weak_ptr<T>& wp)
			:_ptr(wp._ptr)
		{}

		weak_ptr<T>& operator=(const shared_ptr<T>& sp){
			_ptr = sp.get();
			return *this;
		}

		T& operator*(){
			return *_ptr;
		}

		T* operator->(){
			return _ptr;
		}
	public:
		T* _ptr;
	};

删除器

首先我们需要知道new和malloc的关系,delete和free的关系。比如new int[5],其中new的过程中包括malloc+5次构造函数,delete[]的过程包括free+5次析构函数。

但是new的过程中,编译器能通过代码中的[ ]得知该使用几次构造函数,而对应的delete[]是如何知道使用几次析构函数的呢?

下边可以参考vs平台下的机制。

delete[]的ptr的前4个字节中存放了应该使用几次析构函数的次数。也就是在delete[]的时候,ptr并不是delete的起始位置,而是(char*)ptr-4的位置。 如果只是delete的话,起始位置就是ptr。

void test_deleter(){
	// 仿函数对象
	std::shared_ptr<Node> n1(new Node[5], DeleteArray<Node>());
	std::shared_ptr<Node> n2(new Node);

	std::shared_ptr<int> n3(new int[5], DeleteArray<int>());

	std::shared_ptr<int> n4((int*)malloc(sizeof(12)), Free<int>());
}

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

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

相关文章

2024考研408-计算机组成原理第四章-指令系统学习笔记

文章目录 前言一、指令系统现代计算机的结构1.1、指令格式1.1.1、指令的定义1.1.2、指令格式1.1.3、指令—按照地址码数量分类①零地址指令②一地址指令&#xff08;1个操作数、2个操作数情况&#xff09;③二地址指令④三地址指令⑤四地址指令 1.1.4、指令-按照指令长度分类1.…

c#泛型类

派生抽象基类 public abstract class cal<T> {public abstract T add(); }public class ab : cal<string> {public override string add(){return null;} }泛型类的静态成员 public class cal<T> {public static int X9; }public class ab : cal<strin…

detr(detection transformer)模型训练自己的数据集

目录 1.detr源码下载 2. 编译配置 3. 编译报错问题 4. 训练过程打印参数解读 1.detr源码下载 GitHub - facebookresearch/detr: End-to-End Object Detection with Transformers 2. 编译配置 编译参数只需要传递数据集路径即可&#xff0c;数据集格式是coco数据集类型 数…

投稿TMC的感受

投稿任务告与段落了&#xff0c;最终的结果是被TMC给early reject了。这神一样的审稿意见让我真的是老头地铁看手机啊&#xff01;所以虽然TMC没有给我rebuttal的机会。所以我还是打算在CSDN进行一次rebuttal。 其实我做的东西很简单&#xff0c;就是把时间序列中的将时间序列转…

FreeRTOS学习笔记—基础知识

文章目录 一、什么是RTOS二、前后台系统三、实时内核&#xff08;可剥夺型内核&#xff09;四、RTOS系统五、FreeRTOS系统简介六、FreeRTOS源码下载 一、什么是RTOS RTOS全称为:Real Time OS&#xff0c;就是实时操作系统&#xff0c;核心在于实时性。实时操作系统又分为硬实时…

git查看/切换远程仓库

文章目录 一、查看远程仓库地址二、切换远程仓库地址三、整体演示 一、查看远程仓库地址 命令&#xff1a; git remote -v二、切换远程仓库地址 命令&#xff1a; git remote set-url <新的远程仓库地址>三、整体演示

pg报错attempted to delete invisible tuple

问题描述 postgresql数据库执行delete报错&#xff1a;attempted to delete invisible tuple&#xff0c;执行同样条件的select不报错 delete from lzltab1; select count(*) from lzltab1;执行全表删除和全表查询的结果&#xff1a; M# delete from lzltab1; ERROR: 5500…

有一个三位数,他的各个位数的阶乘相加得到这个数

有一个三位数,他的各个位数的阶乘相加得到这个数 1.描述 有一个三位数,它的各个位数的阶乘相加得到这个数 2.代码 输入数据 依次对个位&#xff0c;十位和百位进行拆解计算阶乘 然后相加看看是不是和原来的数据是相等 public class Mian4 {public static void main(String[…

ROS系列报错与解决方法

6.28 一、问题描述 ROS运行roscore命令后发现提示log文件(日志文件)大小超过1G,需要清理 Checking log directory for disk usage. This may take awhile. Press Ctrl-C to interrupt WARNING: disk usage in log directory [/home/wht/.ros/log] is over 1GB. Its recom…

红外线探测防盗报警器电路设计

该报警器能探测人体发出的红外线&#xff0c;当人进入报警器的监视区域内&#xff0c;即可发出报警声&#xff0c;适用于家庭、办公室、仓库、实验室等比较重要场合防盜报警。 一、电路工作原理 电路原理如图4所示。 该装置由红外线传感器、信号放大电路、电压比较器、延时…

这可能是最简单的Page Object库

做过web自动化测试的同学&#xff0c;对Page object设计模式应该不陌生。 Page object库应该根据以下目标开发&#xff1a; Page object应该易于使用 清晰的结构 PageObjects 对于页面对象 PageModules对于页面内容 只写测试&#xff0c;而不是基础。 在可能的情况下防止样…

【AI 充电】KServe + Fluid 加速大模型推理

作者&#xff1a;黄驰琳、露营、车漾 背景 KServe 是 Kubernetes 上的标准模型推理平台&#xff0c;专为高度可扩展的场景而构建&#xff0c;支持现代 Serverless 推理工作负载&#xff0c;用于在任意框架上提供机器学习&#xff08;ML&#xff09;模型服务。它提供高性能、高…

菜鸡shader:L5 fresnel、matcap和cubemap

文章目录 Fresnelshader forge实现UE4蓝图实现 Matcapshader forge实现UE4蓝图实现 CubeMapSD和PS制作所需的贴图shader forge实现unity代码实现UE4蓝图实现(未实现) Fresnel shader forge实现 个人理解是&#xff0c;使用观察方向和法向方向点乘&#xff0c;那就相当于我们的视…

adb-命令大全

目录 一、ADB简介 二、为什么要用ADB 三、ADB架构和原理 四、ADB日志状态 五、ADB常用命令 &#x1f381;更多干货 完整版文档下载方式&#xff1a; 一、ADB简介 ADB全称Android Debug Bridge&#xff0c;起到调试桥的作用&#xff0c;是一个客户端-服务端程序。其中客…

C++数据结构X篇_06_C++单向循环链表实现

本篇参考C单向循环链表实现整理&#xff0c;先搞懂结构框架&#xff0c;后期根据视频利用c对内容实现&#xff0c;也可以对c有更高的提升。 文章目录 1. 链表定义2. 链表插入3. 链表打印(注意打印过程是跳过头节点head的)4. 实验 单向循环链表与单向链表十分相似&#xff0c;具…

ST - NUCLEO-H723ZG ITM不生效的问题

文章目录 ST - NUCLEO-H723ZG ITM不生效的问题概述CubeMX配置点END ST - NUCLEO-H723ZG ITM不生效的问题 概述 在用NUCLEO-H723ZG官方板子做实验, 想用ITM打印一些调试信息出来. 不好使啊. 后来查问题, 发现 NUCLEO-H723ZG上的HSE, LSE的晶振无效, 等于是不能用外部的HSE, LS…

如何找到更多音视频开发学习资料和资源?

如果你对学习音视频开发感兴趣&#xff0c;以下是一些建议&#xff0c;可以帮助你获取更多相关的资料和资源&#xff1a; 在线学习平台&#xff1a;参考一些知名的在线学习平台&#xff0c;如Coursera、Udemy、edX等&#xff0c;搜索他们的课程目录&#xff0c;看是否有与音视频…

【SWAT水文模型】SWAT水文模型建立及应用第六期:SWAT模型率定(SWAT CUP)

SWAT模型率定&#xff08;SWAT CUP&#xff09; 1 准备工作2 水文模型率定&#xff08;SWAT CUP&#xff09;数据准备运行流程 参考 SWAT CUP的下载及安装参见另一博客-【SWAT水文模型】SWAT-CUP安装及使用 下面将具体介绍基于实测水文站数据进行水文模型的率定。 1 准备工作…

【Centos系统故障】虚拟机断电后centos7无法正常启动 XFS(sda3)

1. 背景&#xff1a; 虚拟机断电后centos7无法正常启动 XFS(sda3)&#xff0c;通常都是断电以及非法关机&#xff0c;杀掉虚拟机等突然中断系统的情况&#xff0c;会导致内存数据损坏&#xff0c;从而导致linux操作系统无法启动。 2. 解决办法&#xff1a; 清理掉内存数据&a…

windows10 安装wsl2+docker+php+nginx+mysql

第一步在windows10 上安装docker(可视化桌面) 第二步确定本地电脑开启虚拟化&#xff0c;同时确认下方图内容已经勾选 在cmd下使用命令&#xff1a;wsl --install 安装wsl2。因 wsl2 默认安装ubuntu系统。如果安装wsl2后没有安装ubuntu系统&#xff0c;则需要去谷歌商店里搜索…