【C++11】智能指针问题

news2025/2/25 17:56:44

文章目录

  • RAII
  • 一、auto_ptr
  • 二、unique_ptr
  • 三、shared_ptr
    • shared_ptr的循环引用问题
  • 四、weak_ptr
  • 总结



RAII

RAII就是将资源交给一个对象管理,这个对象能进行正常的管理和释放资源。

一、auto_ptr

auto_ptr的问题是:在拷贝构造和赋值重载时,会将自己资源的管理权转移给对方。

template<class T>
class auto_ptr
{
public:

	auto_ptr(T* ptr)
		:_ptr(ptr)
	{}

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

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

	//ap3(ap1)
	auto_ptr(auto_ptr<T>& ap)
		:_ptr(ap._ptr)
	{
		ap._ptr = nullptr;//必须置空,否则两个指针对象管理同一块空间,会调用析构两次
	}
	
	operator=(auto_ptr<T>& ap)
	{
	    _ptr = ap._ptr;
	    ap._ptr = nullptr;
	}

	~auto_ptr()
	{
		delete _ptr; // _ptr是指针,是内置类型,默认生成的析构函数不处理
		//自定义类型调用它自己的析构
		_ptr = nullptr;
	}
private:
	T* _ptr;
};

这个auto_ptr实际不会用,太挫了。
但面试可能要手撕,也要会。

二、unique_ptr

unique_ptr 简单粗暴地禁止了拷贝和赋值重载,那就解决了上面auto_ptr的管理权转移问题了。

template<class T>
	class unique_ptr
	{
	public:

		unique_ptr(T* ptr)
			:_ptr(ptr)
		{}

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

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

		unique_ptr(unique_ptr<T>& up) = delete;
		unique_ptr<T>& operator=(unique_ptr<T>& up) = delete;
		~unique_ptr()
		{
			delete _ptr; // _ptr是指针,是内置类型,默认生成的析构函数不处理
			//自定义类型调用它自己的析构
			_ptr = nullptr;
		}
	private:
		T* _ptr;
	};

所以unique_ptr就比较适用于,一个指针独占一个资源的情况。

三、shared_ptr

shared_ptr使用了引用计数器,最开始new对象时,计数器为1。
而当其他的同类型的指针对象也指向该对象时,计数器+1。
当有指针对象调用析构函数时,判断计数器是否为0,如果不为0,表明我这个对象还有其他的shared_ptr在管理,不需要delete,让计数器-1即可。
如果计数器为0,表明当前的shared_ptr是最后一个指针对象管理资源了。就delete。

shared_ptr解决了auto_ptr的管理权转移问题,也解决了unique_ptr的不让拷贝和赋值的问题。

注意的问题:

  • 1.不能赋值给自己,否则计数器会++。
    还有一个问题:默认的析构函数的处理方式是delete ptr。
    如果指针管理的资源是T[],或者是fopen,或者是malloc的呢?
    如果还使用默认的处理方式delete ptr,就会出现问题。
    资源是T[],就得用delete[] ,是fopen,就得用fclose,是malloc,就得用free。
    所以在传参的时候,就需要传处理方法过来了。(可以传对象的方法,可以传lambda,可以传包装器。
    所以就需要将析构函数,设置成一个对象/包装器,调用的就是传进来的析构方法。
  • 2.实现赋值重载的时候,有很多细节。
    • 不能自己给自己赋值
    • 在把对方赋值给自己之前,需要把自己所指向的计时器减减,因为我要只向其他的计时器了。如果减减后的计时器为0,说明这块资源只有我一个指针在管理,现在我要只想其他资源,所以我要先释放我现在的资源才能去指向其他资源,否则就会出现内存泄露问题。
      在这里插入图片描述
template<class T>
	class shared_ptr
	{
	public:

		//1) 使用定制删除器,解决一个当new []时,需要delete []的问题
		//2) 还有 使用malloc时,需要free配对
		//3) 使用fopen,需要fclose配对

		template<class D>
		shared_ptr(T* ptr,D del)
			:_ptr(ptr)
			,_pcount(new int(1))
			,_del(del)
		{}
		//然而这里有一个问题,模板D不是全局的,而是只针对这个构造函数
		//所以应该如何创建_del对象呢?

		//使用包装器
		//function <void(T*)> _del;
		//因为不管是上面三种情况的哪一种,共同点都是使用的仿函数的返回值都是void,指针类型都是T*


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

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

		//sp3(sp1)
		shared_ptr(shared_ptr<T>& sp)
			:_ptr(sp._ptr)
			,_pcount(sp._pcount) //让他们的引用计数器指针指向同一个计数器
		{
			++(*_pcount);
		}

		//sp5 = sp2
		shared_ptr<T>& operator=(shared_ptr<T>& sp)
		{
			//不能自己赋值给自己,否则引用计数器会++
			//这样判断是可以的,只要不同的对象指向同一个资源空间,这几个对象之间的赋值都是相当于自己给自己赋值
			if(_ptr == sp._ptr)  
			//if (_pcount == sp._pcount) //这也可以
			{
				return *this;
			}
			//赋值前,需要将之前的计数器--,如果计数器为0,就释放资源
			//因为我要指向其他资源了,我原来的资源的计数器当然要--
			if (--(*sp._pcount) == 0)
			{
				_del(sp._ptr);
				delete sp._pcount;
			}

			_ptr = sp._ptr;
			_pcount = sp._pcount;

			++(*_pcount);

			return *this;
		}

		~shared_ptr()
		{
			if (--(*_pcount) == 0)
			{
				_del(_ptr);
				delete _pcount;
				_pcount = nullptr;
				_ptr = nullptr;
			}
		}

		T* get() const 
		{
			return _ptr;
		}

		int use_count() const 
		{
			return *_pcount;
		}
	private:
		T* _ptr;
		int* _pcount;
		//包装器
		//包装器包装的_del是D类型,这个D也不知道是什么类型,
		//包装器可以包装仿函数,包装对象的方法等。
		//如果只是普通的指针析构,那就给一个缺省。
		function <void(T*)> _del = [](T* ptr) {delete ptr; }; 
		//dzt::shared_ptr<int> svr(new int); //这样的方式,就是没传处理方式,那默认就是用缺省。
		//dzt::shared_ptr<int> svr(new int[10],[](int* ptr){delete[] ptr};
		//这样的传参方式,就用delete[]解决
	};

shared_ptr的循环引用问题

在这里插入图片描述
循环引用问题是两个节点的shared_ptr指针互相指向的时候,引用计数器都会+1,这就出现了互相的计数器都是2.在析构时,都变成1,此时就出现了,到底谁先析构的问题。
就一直死循环了。

不过,shared_ptr能解决日常生活99%的问题,它只有一个缺点,就是循环引用问题。

解决办法:weak_ptr,weak_ptr是专门解决循环引用这个问题的。
weak_ptr没有RAII,没有引用计数,不参与对象资源的管理和释放,只是一个简单的访问资源的操作。

四、weak_ptr

template<class T>
	class weak_ptr
	{
	public:

		weak_ptr()
			:_ptr(nullptr)
		{}
		weak_ptr(T* ptr)
			:_ptr(ptr)
		{}

		//weak_ptr必须支持一个shared_ptr构造weak_ptr的构造函数
		weak_ptr(const shared_ptr<T>& sp)
			:_ptr(sp.get())
		{}

		weak_ptr<T>& operator=(const shared_ptr<T>& sp)
		{
			//类外面无法直接访问类的私有成员
			_ptr = sp.get();
			return *this;
		}

		//weak只支持访问资源的操作
		T& operator*()
		{
			return *_ptr;
		}

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

		//weak_ptr不参与对象资源的管理和释放,所以没有析构函数
		
	private:
		T* _ptr;
	};

总结

本文讲述了几个智能指针的优缺点和模拟实现。

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

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

相关文章

Vue39-组件的嵌套

一、嵌套的定义 二、嵌套示例 school组件中&#xff0c;嵌套student组件 局部注册&#xff0c;注册给谁&#xff0c;就在谁的结构里面写&#xff01; vue拿不到<student>组件标签&#xff01;&#xff01;&#xff01; 三、一人之下万人之上的app组件 或者容器里面不用…

飞速(FS)InfiniBand光模块和线缆指南

InfiniBand网络架构因其高速、低延迟和可扩展性优势&#xff0c;广泛应用于高性能计算&#xff08;HPC&#xff09;。飞速&#xff08;FS&#xff09;提供多样化的InfiniBand光模块和线缆&#xff0c;为客户提供高性能InfiniBand网络解决方案&#xff0c;以建立高效可靠的网络连…

如何保护云主机安全

在数字化时代&#xff0c;云服务器已成为企业数据存储、处理和传输的重要工具。然而&#xff0c;随着其应用的广泛和深入&#xff0c;云服务器也面临着越来越多的安全威胁。为了应对这些威胁&#xff0c;白名单技术应运而生&#xff0c;成为保护云服务器安全的重要手段。 首先&…

(done) 什么是 perplexity 困惑度?

参考&#xff1a;https://www.youtube.com/watch?vB_2bntDYano 困惑度 perplexity 是一种用来衡量语言模型性能的度量&#xff0c;类似于交叉熵。 困惑度越低越好&#xff0c;越低说明一个模型越好。 一个典型的公式在下面&#xff1a;

基于机器学习的C-MAPSS涡扇发动机RUL预测

美国国家航空航天局的商用模块化航空推进仿真系统&#xff08;CMAPSS&#xff09;所模拟出的涡扇发动机性能退化数据进行实验验证&#xff0c;数据中包含有风扇、涡轮、压气机等组件参数。C-MAPSS中所包含的数据集可以模拟出从海平面到42千英尺的高度&#xff0c;从0到0.9马赫的…

前端学习CSS之神奇的块浮动

在盒子模型的基础上就可以对网页进行设计 不知道盒子模型的可以看前面关于盒子模型的内容 而普通的网页设计具有一定的原始规律,这个原始规律就是文档流 文档流 标签在网页二维平面内默认的一种排序方式,块级标签不管怎么设置都会占一行,而同一行不能放置两个块级标签 行级…

LeetCode | 434.字符串中的单词数

这道题直接使用语言内置的 split 函数可直接分离出字符串中的每个单词&#xff0c;但是要注意区分两种情况&#xff1a;1、空串&#xff1b;2、多个空格连续&#xff0c;分割后会出现空字符的情况&#xff0c;应该舍弃 class Solution(object):def countSegments(self, s):&qu…

MySQL损坏,使用data恢复数据

MySQL损坏&#xff0c;重装MySQL使用data文件恢复数据库 1.清空相关注册表(清空安装残留)2.下载合适MySQL版本(与损坏数据库版本相同)3.数据恢复4.Windows server MySQL备份bat5.设置Windows定时执行 # 初始化安装 mysqld -install# 查看数据初始化密码 mysqld --initialize --…

Commons-Collections篇-CC4链分析

前言 因为 CommonsCollections4 除 4.0 的其他版本去掉了 InvokerTransformer 继承 Serializable&#xff0c;导致该方法无法序列化。 同时 CommonsCollections 4的版本 TransformingComparator 继承了 Serializable接口&#xff0c;而CommonsCollections 3里是没有的&#xf…

183.二叉树:二叉搜索树中的众数(力扣)

代码解决 /*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* TreeNode *right;* TreeNode() : val(0), left(nullptr), right(nullptr) {}* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}* Tre…

WordPress实时搜索插件Ajax Search Lite,轻松替代默认搜索功能

WordPress自带的默认搜索功能是跳转到搜索结果页&#xff0c;如果你想要实时搜索功能&#xff0c;特别是在问答中心显示搜索功能&#xff0c;那么建议使用这个WordPress实时搜索插件Ajax Search Lite&#xff0c;它可以在文章、页面、自定义类型文章中搜索标题、内容、摘要、自…

图解Attention学习笔记

教程是来自https://github.com/datawhalechina/learn-nlp-with-transformers/blob/main/docs/ 图解Attention Attention出现的原因是&#xff1a;基于循环神经网络&#xff08;RNN&#xff09;一类的seq2seq模型&#xff0c;在处理长文本时遇到了挑战&#xff0c;而对长文本中…

重生之 SpringBoot3 入门保姆级学习(21、场景整合 Redis 定制对象序列化存储)

重生之 SpringBoot3 入门保姆级学习&#xff08;21、场景整合 Redis 定制对象序列化存储&#xff09; 6.4 定制化 6.4 定制化 需求&#xff1a;保存一个 Person 对象到 redis 创建 Person 类 package com.zhong.redis.entity;import lombok.AllArgsConstructor; import lombok…

13.ChatGPT 大模型训练核心技术

ChatGPT 大模型训练核心技术 从 GPT-3 到 ChatGPT 的大模型训练技术演进 基于RLHF训练大模型的三阶段 • Domain Specific Pre-Training: Fine-tune a pre-trained LLM on raw text with a Causal Language Modelling Objective.• Supervised fine-tuning: Fine-tune the do…

Confluence是否免费?你需要知道的都在这里!

根据Atlassian官方信息&#xff0c;可以确定的是 Confluence 并不免费&#xff0c;但为10人以下团队提供了免费版本。免费版可以使用不限量的页面、空间&#xff0c;但只有2GB的存储空间和3个活动白板。但国内有不少公司通过使用破解版的方式来免费使用Cofluence。下面本文将详…

排阻A103J

1.定义&#xff1a; 排阻&#xff0c;即网络电阻器。排阻是将若干个参数完全相同的电阻集中封装在一起&#xff0c;组成制成的。排阻一般应用在数字电路上&#xff0c;比如&#xff1a;作为某个并行口的上拉或者下拉电阻用。使用排阻比用若干只固定电阻更方便。 排阻是将若干…

vue+intro.js实现引导功能

前言&#xff1a; 使用 intro.js这个插件&#xff0c;来实现一个引导性的效果&#xff0c;适用场景&#xff0c;比如&#xff1a;新手引导页&#xff0c;操作说明等等 效果图&#xff1a; 官网地址&#xff1a;点我 实现步骤&#xff1a; 1、安装 npm install intro.js --sa…

Spring Boot集成Timefold Solver实现课程表编排

1. 什么是Timefold Solver&#xff1f; 每个组织都面临规划问题&#xff1a;使用一组有限的资源&#xff08;员工、资产、时间和金钱&#xff09;提供产品或服务。Timefold Solver 优化了此类规划&#xff0c;以更少的资源开展更多业务。这被称为约束满足编程&#xff08;属于…

LeetCode | 168.Excel表列名称

这道题一开始以为是简单的进制转换问题&#xff0c;用的以往的思路&#xff0c;对于一般性的进制转换题目&#xff0c;只需要不断地对 columnNumber 进行 % 运算取得最后一位&#xff0c;然后对 columnNumber 进行 / 运算&#xff0c;将已经取得的位数去掉&#xff0c;直到 col…

ABB 和PLC ProfinN 通信

1,设置IP 地址 2&#xff0c; 设置站名称 3&#xff0c; 修改传送区大小 4,配置DI DO 5,导出 6&#xff0c;安装gsd 文件 7&#xff0c;建立通信