C++进阶--智能指针

智能指针的概念

智能指针是C++中的一个重要概念,用于管理动态分配的对象内存它是一个类模板,通过封装原始指针,并在对象生命周期结束时自动释放内存,从而避免了内存泄漏和资源管理的繁琐工作

C++标准库提供了多种常见的智能指针,目前常用的有:unique_ptr , shared_ptr , weak_ptr。(头文件: < memory >

为什么需要智能指针?

看一个例子:

int div()
{
	int a, b;
	cin >> a >> b;
	if (b == 0)
		throw invalid_argument("除0错误");
	return a / b;
}
void Func()
{
	// 1、如果p1这里new 抛异常会如何?
	// 2、如果p2这里new 抛异常会如何?
	// 3、如果div调用这里又会抛异常会如何?
	int* p1 = new int;
	int* p2 = new int;
	cout << div() << endl;
	delete p1;
	delete p2;
}
int main()
{
	try
	{
		Func();
	}
	catch (exception& e)
	{
		cout << e.what() << endl;
	}
	return 0;
}

在这里插入图片描述

RAll

RAII(Resource Acquisition Is Initialization)的核心思想是资源的获取和释放应该与对象的生命周期绑定在一起。当一个对象被创建时,它应该获取所需要的资源;当对象被销毁时,它应该释放已经获取的资源。这样可以确保资源在不再需要时被正确释放,从而避免资源泄漏。

利用RAll原理做出智能指针

代码:

template<class T>
class SmartPtr
{
public:
	//RALL
	SmartPtr(T* ptr)
		:_ptr(ptr)
	{}

	~SmartPtr()
	{
		cout << "delete: " << _ptr<< endl;
		delete _ptr;
	}

	//解引用
	T& operator*()
	{
		return *_ptr;
	}
	//指针形式
	T* operator->()
	{
		return _ptr;
	}
private:
	T* _ptr;
};

利用RAll原理制作智能指针:
在这里插入图片描述
在这里插入图片描述

测试:在这里插入图片描述
在这里插入图片描述

auto_ptr

在这里插入图片描述

简单使用

在这里插入图片描述
在这里插入图片描述
由于这种智能指针不能对实际应用起到作用,所以现在大多数程序员都没有用到它。

要模拟实现一个auto_ptr时,只需要在上面代码的赋值拷贝中加上:
在这里插入图片描述

unique_ptr

unique_ptr与常规指针的一个主要区别就是:它拥有对指向对象的独占拥有权。这意味着同一时间只能有一个unique_ptr指向某个对象,不能进行复制操作,只能进行移动操作。当unique_ptr被销毁或重置时,他所指向的对象也会自动删除。

模拟实现

template<class T>
	class unique_ptr
	{
	public:
		unique_ptr(T* ptr)
			:_ptr(ptr)
		{}

		unique_ptr(unique_ptr<T>& p)= delete;
		unique_ptr operator=(const unique_ptr<T>& p) = delete;

		~unique_ptr()
		{
			cout << "delete:" << _ptr << endl;

			delete _ptr;
		}

		// 像指针一样
		T& operator*()
		{
			return *_ptr;
		}

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

实现时只需要对默认拷贝构造和默认赋值构造给它删除了,那么就实现了不能对它进行复制了。

测试:
在这里插入图片描述

shared_ptr

在这里插入图片描述

为什么不能用static成员来进行计数?

在这里插入图片描述

模拟实现

template<class T>
	class shared_ptr
	{
	public:
		//常规使用的构造函数
		shared_ptr(T* ptr = nullptr)
			: _ptr(ptr),
			_count(new int(1))
		{}

		//sp2(sp1)
		shared_ptr(const shared_ptr<T>& sp)
		{
			_ptr = sp._ptr;
			_count = sp._count;

			//拷贝后将count+1
			++(*_count);
		}

		//sp3=sp1
		shared_ptr<T>& operator=(const shared_ptr<T>& sp)
		{
			if (_ptr != sp._ptr)
			{
				//使用赋值时,要考虑之前的智能指针指向的空间
				release();
				_ptr = sp._ptr;
				_count = sp._count;
				//拷贝后将count+1
				++(*_count);
			}

			return *this;
		}
		//释放资源
		void release()
		{
			if (--(*_count) == 0)
			{
				cout << "delete:" << _ptr << endl;
				delete _ptr;
				delete _count;
			}
		}
		~shared_ptr()
		{
			//当count为0时触发
			release();
		}

		int use_count()
		{
			return *_count;
		}

		// 像指针一样
		T& operator*()
		{
			return *_ptr;
		}

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

		T* get() const
		{
			return _ptr;
		}
	private:
		T* _ptr;
		int* _count;
	};

解释:
在这里插入图片描述

测试:
在这里插入图片描述

赋值时必须要对之前智能指针指向的空间进行检查:
在这里插入图片描述

验证解引用:
在这里插入图片描述

定制删除器

在这里插入图片描述
在类中添加一个成员:
在这里插入图片描述
在这里插入图片描述
添加一个构造函数的重载:
在这里插入图片描述
在这里插入图片描述
释放时的修改:
在这里插入图片描述

template <class T>
struct ArrayDelete
{
	void operator()(T* ptr)
	{
		delete[] ptr;
	}
};
//定制删除器
int main()
{
	fnc::shared_ptr<ListNode> n(new ListNode(10));
 	fnc::shared_ptr<ListNode> n1(new ListNode[10],ArrayDelete<ListNode>());
	fnc::shared_ptr<FILE> n2(fopen("FileName.cpp", "r"), [](FILE* file) {fclose(file); });
}

在这里插入图片描述

智能指针的缺陷

struct ListNode
{
	int _val;

	fnc::shared_ptr<ListNode> _next;
	fnc::shared_ptr<ListNode> _prev;
	
	ListNode(int val=0)
		:_val(val)
	{}
};

int main()
{
	fnc::shared_ptr<ListNode> n1(new ListNode(10));
	fnc::shared_ptr<ListNode> n2(new ListNode(20));

	cout << n1.use_count() << endl;
	cout << n2.use_count() << endl;

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

	cout << n1.use_count() << endl;
	cout << n2.use_count() << endl;

	//delete n1;
	//delete n2;

	return 0;
}

结果:
在这里插入图片描述
在这里插入图片描述

weak_ptr

weak_ptr就是用于解决循环引用的问题。它与shared_ptr配合使用,可以指向一个由shared_ptr管理的对象,但不会增加该对象的引用计数。

weak_ptr不拥有所指对象的拥有权,当指向对象被释放时,weak_ptr会自动失效,不再指向有效的对象

	template<class T>
	class weak_ptr
	{
	public:
		// RAll
		weak_ptr()
			:_ptr(nullptr)
		{}

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

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

		// 像指针一样
		T& operator*()
		{
			return *_ptr;
		}

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

	private:
		T* _ptr;
	};

将LIstNode的next指针和prev指针类型改为weak_ptr的:
在这里插入图片描述
结果:
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

el-date-picker 禁用时分秒选择(包括禁用下拉框展示)

2024.04.26今天我学习了对el-date-picker进行禁用时分秒&#xff0c; 在使用el-date-picker组件的时候&#xff0c;我们有可能遇到需要把时分秒的时间固定&#xff0c;然后并且不能让他修改&#xff1a; 1714120999296 比如右上角的这个时间&#xff0c;我们要给它固定是‘08:…

Open3D(C++) 最小二乘拟合多项式曲线

目录 一、算法原理二、代码实现三、结果展示本文由CSDN点云侠原创,原文链接。如果你不是在点云侠的博客中看到该文章,那么此处便是不要脸的爬虫与GPT。 一、算法原理 多项式曲线表示为: p ( x ) =

使用Screenshots安装Fedora 40版本详细教程

Fedora 40是Fedora操作系统的最新版本&#xff0c;于 2024 年 4 月 23 日发布&#xff0c;是一个社区支持的 Linux 发行版&#xff0c;以其创新功能、领先技术和活跃的社区支持而闻名。 在本指南中&#xff0c;我们将引导您完成安装Fedora 40 Server的分步过程&#xff0c;确保…

SystemUI KeyButtonView setDarkIntensity 解析

继承自 ImageView KeyButtonDrawable intensity为0时按键颜色为白色。 intensity为1时黑色为的调用堆栈&#xff1a; java.lang.NullPointerException: Attempt to invoke virtual method int java.lang.String.length() on a null object referenceat com.android.systemui.…

Linux网络编程---多路I/O转接服务器(一)

多路I/O转接服务器 多路IO转接服务器也叫做多任务IO服务器。该类服务器实现的主旨思想是&#xff0c;不再由应用程序自己监视客户端连接&#xff0c;取而代之由内核替应用程序监视文件。 主要使用的方法有三种&#xff1a;select、poll、epoll 一、select多路IO转接 让内核去…

Java 网络编程之TCP(三):基于NIO实现服务端,BIO实现客户端

前面的文章&#xff0c;我们讲述了BIO的概念&#xff0c;以及编程模型&#xff0c;由于BIO中服务器端的一些阻塞的点&#xff0c;导致服务端对于每一个客户端连接&#xff0c;都要开辟一个线程来处理&#xff0c;导致资源浪费&#xff0c;效率低。 为此&#xff0c;Linux 内核…

边缘计算在视频监控领域的应用

一、边缘计算在视频监控领域的应用 运用边缘计算解决视频监控问题&#xff0c;可以带来许多优势。以下是一些具体的应用示例&#xff1a; 实时分析与处理&#xff1a;在视频监控系统中&#xff0c;边缘计算盒子可以实时处理和分析视频流&#xff0c;实现对监控画面的智能识别…

BGP选路实验(锐捷)---AS-PATH选路

实验拓扑图 基本配置如图所示 要求&#xff1a;R8上利用loopback口建立多个分段ip&#xff0c;利用bgp选路原则让双网段数据通过R6转发&#xff0c;单网段数据通过R7转发&#xff0c;这里添加as-path号建议添加自己的bgp所属的as号&#xff0c;以防止修改as-path后影响as-path…

❤️新版Linux零基础快速入门到精通——第二部分❤️

❤️新版Linux零基础快速入门到精通——第二部分❤️ 非科班的我&#xff01;Ta&#xff01;还是来了~~~2. Linux基础命令2.1 类Unix系统目录结构2.2 Linux目录结构2.2.1 Linux用户目录2.2.2 Linux目录练习 2.3 Linux 命令入门2.3.1 命令基础2.3.1.1 help2.3.1.2 man(manual)2.…

Windows Vscode ModuleNotFoundError: No module named

故障现象&#xff1a; Windows Vscode 经常会遇到模块路径查找失败的异常。 如运行2_from_import_test.py后&#xff0c;报错&#xff1a; 发生异常: ModuleNotFoundError No module named programmer File "D:\leolab\programmer\2_from_import_test.py", line 8…

虚拟机VMware下ROS Neotic(Ubuntu 20.04)下安装OpenCV

一、ROS安装 ROS的官方安装步骤&#xff1a; 1、noetic / Ubuntu 20.04 &#xff1a; http://wiki.ros.org/noetic/Installation/Ubuntu 2、melodic / Ubuntu 18.04&#xff1a; http://wiki.ros.org/melodic/Installation/Ubuntu 3、kinetic / Ubuntu 16.04&#xff1a; http:…

C语言:一维数组、二维数组、字符数组介绍

数组 介绍一维数组定义应用方法初始化 举例示例结果 二维数组定义应用方法初始化 举例示例结果 字符数组定义应用方法初始化 举例示例结果分析 介绍 在C语言中&#xff0c;数组是一种基本的数据结构&#xff0c;用于存储一系列相同类型的数据。数组可以是多维的&#xff0c;最…

phpstorm 设置变量,自动补全代码

效果 进入设置->实时模板->PHP->添加 添加动态模板->完善写法 定义->选择PHP->应用就行

什么是宏观经济的先行指标、同步指标与滞后指标

宏观经济波动是一种周期性的繁荣、衰退、萧条、复苏循环变化过程&#xff0c;在这种变动中&#xff0c;不同经济指标的变动并非总与宏观经济运行步调一致。按统计指标变动轨迹与宏观经济变动轨迹的时间关系,可以将其划分为先行指标、同步指标和滞后指标。 一、概念和作用 先行…

JetBrains CLion v2023.3.4 激活版 (C/C++ 集成开发IDE)

前言 JetBrains CLion是一款跨平台的C/C集成开发环境&#xff0c;由JetBrains公司推出。其最新版本支持C14几乎完全&#xff0c;并初步支持C17&#xff0c;使得编写代码更加便捷。CLion还提供了Disassembly view&#xff08;反汇编视图&#xff09;&#xff0c;即使没有源代码…

《欢乐钓鱼大师》攻略:怎么在竞标赛中获得高分?

《欢乐钓鱼大师》锦标赛是游戏中的一项激动人心的钓鱼比赛活动&#xff0c;而在这场比赛中&#xff0c;如何获得高分成为了每位钓手追求的目标。在这篇攻略中&#xff0c;我们将为您详细介绍如何通过优化鱼竿、管理体力、利用buff和词条以及前期准备等方面来提高您在锦标赛中的…

信号分解 | RLMD(鲁棒性局部均值分解)-Matlab

分解效果 RLMD(鲁棒性局部均值分解) RLMD(鲁棒性局部均值分解)-Matlab 代码实现 % %% 清除所有变量 关闭窗口 clc clear all close all%% 导入数据 % data = xlsread(Data.xlsx);%% 输入信号%% RLMD分解 %参数进行设置 % options.display =

【React】CSS 局部样式

书写 CSS 的时候&#xff0c;如果 CSS 文件名包含 module&#xff0c;那么说明该 CSS 是一个局部 CSS 样式文件&#xff0c;类似于 vue 中的 scoped。 .avatarContainer {width: 40px;height: 40px;border-radius: 50%;background: rgb(213, 226, 226); }import styles from ..…

【Redis 开发】缓存雪崩和缓存击穿

缓存问题 缓存雪崩解决方案 缓存击穿互斥锁逻辑时间基于互斥锁解决缓存击穿问题基于逻辑过期方式解决缓存击穿问题 缓存雪崩 缓存雪崩是指在同一时间段&#xff0c;大量的缓存key同时失效或者Redis服务器宕机&#xff0c;导致大量请求到达数据库&#xff0c;带来巨大压力 解决…

游戏发行困境及OgGame云游戏解决方案简述

随着全球化浪潮的持续推进&#xff0c;中国游戏开发者们不再满足于国内市场的发展&#xff0c;而是开始将目光投向更为广阔的海外市场。这一趋势的崛起背后&#xff0c;是中国企业意识到国际化是其发展的必由之路&#xff0c;也是游戏行业突破国内困境的体现。本文将简要阐述游…