【c++智能指针】

news2025/7/19 6:36:30

目录

  • 一、智能指针的使用及原理
  • 二、auto_ptr
  • 三、unique_ptr
  • 三、shared_ptr
  • 四、weak_ptr
  • 五、定制删除器

一、智能指针的使用及原理

RAII(Resource Acquisition Is Initialization)是一种利用对象生命周期来控制程序资源(如内存、文件句柄、网络连接、互斥量等等)的简单技术。在对象构造时获取资源,接着控制对资源的访问使之在对象的生命周期内始终保持有效,最后在对象析构的时候释放资源。借此,我们实际上把管理一份资源的责任托管给了一个对象。这种做法有两大好处:
不需要显式地释放资源。
采用这种方式,对象所需的资源在其生命期内始终保持有效。
比如:为什么智能指针前:

#include"smartptr.h"
class A
{
public:
	A(int a)
		:_a(a)
	{
		//cout << "A(int a)" << endl;
	}

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

private:
	int _a;
};
int div()
{
	int a, b;
	cin >> a >> b;
	if (b == 0)
		throw invalid_argument("除0错误");
	return a / b;
}
void Func()
{
	//Ting::SmartPtr<A> sp1(new A(10));
	//Ting::SmartPtr<A> sp2(new A(20));
	A* a = new A(10);
	cout << div() << endl;
}
int main()
{
	try {
		Func();
	}
	catch (const exception& e)
	{
		cout << e.what() << endl;
	}
	return 0;
}

在这里插入图片描述
使用后:

#include<iostream>
using namespace std;

namespace Ting
{
	template<class T>
	class SmartPtr
	{
	public:
		SmartPtr(const T* ptr)
			:_ptr(ptr)
		{}

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

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

		~SmartPtr()
		{
			if (_ptr)
			{
				delete _ptr;
			}
		}

	private:
		const T* _ptr;
	};
}
#include"smartptr.h"
class A
{
public:
	A(int a)
		:_a(a)
	{
		//cout << "A(int a)" << endl;
	}

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

private:
	int _a;
};
int div()
{
	int a, b;
	cin >> a >> b;
	if (b == 0)
		throw invalid_argument("除0错误");
	return a / b;
}
void Func()
{
	Ting::SmartPtr<A> sp1(new A(10));
	Ting::SmartPtr<A> sp2(new A(20));
	//A* a = new A(10);
	cout << div() << endl;
}
int main()
{
	try {
		Func();
	}
	catch (const exception& e)
	{
		cout << e.what() << endl;
	}
	return 0;
}

在这里插入图片描述

二、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 (_ptr != ap._ptr)
		{
			if (_ptr)
			{
				delete _ptr;
			}
			_ptr = ap._ptr;
			//悬空
			ap._ptr = nullptr;
		}
		return *this;

	}

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

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

	~auto_ptr()
	{
		if (_ptr)
		{
			delete _ptr;
		}
	}

private:
	T* _ptr;
};
 // 结论:auto_ptr是一个失败设计,很多公司明确要求不能使用auto_ptr
 //int main()
 //{
 //  std::auto_ptr<int> sp1(new int);
 //  std::auto_ptr<int> sp2(sp1); // 管理权转移
//
 //  // sp1悬空
//  *sp2 = 10;
 //  cout << *sp2 << endl;
 //  cout << *sp1 << endl;
 //  return 0;
 //}

三、unique_ptr

为了解决上面auto_ptr拷贝赋值后因为管理权限转移而造成悬空问题,C++11引入了unique_ptr
unique_ptr的实现原理:简单粗暴的防拷贝,下面简化模拟实现了一份UniquePtr来了解它的原理

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

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


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

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

		~unique_ptr()
		{
			if (_ptr)
			{
				delete _ptr;
			}
		}

	private:
		T* _ptr;
	};

三、shared_ptr

C++11中开始提供更靠谱的并且支持拷贝的shared_ptr
shared_ptr的原理:是通过引用计数的方式来实现多个shared_ptr对象之间共享资源。

  1. shared_ptr在其内部,给每个资源都维护了着一份计数,用来记录该份资源被几个对象共享。
  2. 在对象被销毁时(也就是析构函数调用),就说明自己不使用该资源了,对象的引用计数减一。
  3. 如果引用计数是0,就说明自己是最后一个使用该资源的对象,必须释放该资源;
  4. 如果不是0,就说明除了自己还有其他对象在使用该份资源,不能释放该资源,否则其他对象就成野指针了。
    下面简化模拟实现了一份shared_ptr来了解它的原理
template<class T>
class shared_ptr
{
public:
	shared_ptr(T* ptr)
		:_ptr(ptr)
		,_pcount(new int(1))
	{}

	shared_ptr(shared_ptr<T>& sp)
		:_ptr(sp._ptr)
		,_pcount(sp._pcount)
	{
		++(*_pcount);
	}

	shared_ptr<T>& operator=(shared_ptr<T>& sp)
	{
		//防止自己给自己赋值
		if (_ptr != sp._ptr)
		{
			if (_ptr)
			{
				delete _ptr;
			}
			_ptr = sp._ptr;
			_pcount = sp._pcount;

			++(*_pcount);
		}
		return *this;
	}

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

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

	T& operator*()
	{
		return *_ptr;
	}
private:
	T* _ptr;
	int* _pcount;
};

但是shared_ptr再有些场景下会出现循环引用问题。如:
在这里插入图片描述
没有调用Node的析构函数。为什么呢?如图:
在这里插入图片描述

四、weak_ptr

为了解决shared_ptr循环引用场景,所以又引入了weak_ptr。
在这里插入图片描述
下面简化模拟实现了一份weak_ptr来了解它的原理

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

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

	weak_ptr<T>& operator=(Ting::SmartPtr<T>& sp)
	{
		_ptr = sp.get();
		return *this;
	}

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

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

private:
	T* _ptr;
};

五、定制删除器

上面模拟的shared_ptr只能在少部分场景下使用,在这个场景下使用不了。如:
在这里插入图片描述
所以我们可以模拟库里在构造时加个仿函数过去。
在这里插入图片描述
属性里也加个,用包装器来申明。
在这里插入图片描述
析构函数改成调用仿函数。
在这里插入图片描述
完整代码:

template<class T>
class shared_ptr
{
public:
	shared_ptr(T* ptr)
		:_ptr(ptr)
		, _pcount(new int(1))
	{}

	template<class D>
	shared_ptr(T* ptr,D del)
		:_ptr(ptr)
		,_pcount(new int(1))
		,_del(del)
	{}

	shared_ptr(shared_ptr<T>& sp)
		:_ptr(sp._ptr)
		,_pcount(sp._pcount)
	{
		++(*_pcount);
	}

	shared_ptr<T>& operator=(shared_ptr<T>& sp)
	{
		//防止自己给自己赋值
		if (_ptr != sp._ptr)
		{
			if (_ptr)
			{
				delete _ptr;
			}
			_ptr = sp._ptr;
			_pcount = sp._pcount;

			++(*_pcount);
		}
		return *this;
	}

	~shared_ptr()
	{
		if (--(*_pcount) == 0)
		{

			_del(_ptr);
			delete _pcount;
		}
	}

	T* get()
	{
		return _ptr;
	}

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

	T& operator*()
	{
		return *_ptr;
	}
private:
	T* _ptr;
	int* _pcount;
	function<void(T*)> _del = [](T* ptr) {delete ptr; };
};

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

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

相关文章

新型的终端复用器 tmux

以前遇到长时间执行任务时&#xff0c;一般是使用nohup加后台运行&#xff0c;但是涉及到少量代码编写。 同事介绍了一个screen命令&#xff0c;根据文档&#xff0c;此命令已经过时&#xff0c;最新的命令是tmux。 tmux的介绍文档&#xff0c;RedHat的这一篇非常不错。 在文…

vue ref和$refs获取dom元素

vue ref和$refs获取dom元素 **创建 工程&#xff1a; H:\java_work\java_springboot\vue_study ctrl按住不放 右键 悬着 powershell H:\java_work\java_springboot\js_study\Vue2_3入门到实战-配套资料\01-随堂代码素材\day04\准备代码\14-ref和$refs获取dom对象 vue --ve…

Kotlin中的数值类型

在Kotlin中&#xff0c;Byte、Short、Int、Long、Float和Double是基本数据类型&#xff0c;用于表示不同范围和精度的数值。 Byte&#xff08;字节&#xff09;&#xff1a;Byte类型是8位有符号整数类型&#xff0c;取值范围为-128到127。在Kotlin中&#xff0c;可以使用字面值…

《深入浅出OCR》第三章:OCR文字检测

✨专栏介绍: 经过几个月的精心筹备,本作者推出全新系列《深入浅出OCR》专栏,对标最全OCR教程,具体章节如导图所示,将分别从OCR技术发展、方向、概念、算法、论文、数据集等各种角度展开详细介绍。 👨‍💻面向对象: 本篇前言知识主要介绍深度学习知识,全面总结知知识…

Linux Zabbix企业级监控平台+cpolar实现远程访问

文章目录 前言1. Linux 局域网访问Zabbix2. Linux 安装cpolar3. 配置Zabbix公网访问地址4. 公网远程访问Zabbix5. 固定Zabbix公网地址 前言 Zabbix是一个基于WEB界面的提供分布式系统监视以及网络监视功能的企业级的开源解决方案。能监视各种网络参数&#xff0c;保证服务器系…

C++stack和queue模拟实现以及deque的介绍

stack和queue介绍以及模拟实现 1.stack1.1stack的介绍1.2stack的使用 2.queue2.1queue的介绍2.2queue的使用 3.容器适配器3.1什么是适配器 4.stack模拟实现5.queue的模拟实现6.deque&#xff08;双端队列&#xff09; 1.stack 1.1stack的介绍 stack的文档介绍 stack是一种容…

软信天成:流程管理是企业精细化管理的一大利器

流程管理&#xff08;BPM&#xff09;是指组织和管理内部或跨部门的工作流程&#xff0c;主要包括设计、建模、执行、监控和优化业务流程&#xff0c;确保工作按照标准化的步骤进行&#xff0c;从而提高效率、降低成本&#xff0c;促进业务增长。 一、流程管理生命周期五大步骤…

xml文件报错 ORA-00907: 缺失右括号

原来的sql 更改之后 加一个select * from &#xff08;&#xff09;

VScode platformio的使用

一、platformio 工程创建 打开vscode界面你会发现左下多了个家的小图标&#xff0c;点击这里就可以进入platformio。 在右侧Quick Access栏中&#xff0c;有4个选项。可以看得出来&#xff0c;我们这里直接点击创建一个新的工程。 点击New Project打开project配置界面&#x…

Android---自定义View

当 Android SDK 中提供的系统 UI 控件无法满足业务需求时&#xff0c;需要考虑自己实现 UI 控件。掌握自定义控件&#xff0c;是理解整套 Android 渲染体系的基础。自定义 UI 控件有2种方式&#xff1a; 继承系统提供的成熟控件&#xff08;比如 LinearLayout、RelativeLayout、…

美国跨境金融科技公司【Zolve】完成1亿美元融资

来源&#xff1a;猛兽财经 作者&#xff1a;猛兽财经 猛兽财经获悉&#xff0c;总部位于美国纽约的跨境金融科技公司Zolve近期宣布已经完成1亿美元融资。 本轮融资由CIM投资 该公司打算将这笔资金用于在英国、加拿大和澳大利亚的进一步扩张。 Zolve由创始人Raghunandan G领导&…

自由程序员想接私活?那你还不得知道这几个接单平台!最后一个就是宝藏!!

相信喜欢搞钱的程序员都知道&#xff0c;平常在平台上接点私活&#xff0c;利用闲暇时间接单是搞钱的常用套路&#xff0c;可是你确定你选对平台了吗&#xff1f;不管你是刚准备接单的小白&#xff0c;还是已经干了一段时间的老油条&#xff0c;都建议你看完本期文章&#xff0…

Spring框架(三)

1、代理模式&#xff1a; 二十三种设计模式中的一种&#xff0c;属于结构型模式。它的作用就是通过提供一个代理类&#xff0c;让我们在调用目标方法的时候&#xff0c;不再是直接对目标方法进行调用&#xff0c;而是通过代理类间接调用。让不属于目标方法核心逻辑的代码从目标…

【译】快速开始 Compose 跨平台项目

原文&#xff1a; Compose Multiplatform application 作者&#xff1a;JetBrains 注意 Compose Multiplatform 中的 iOS 部分目前处于 Alpha 状态。以后可能会有不兼容的更改&#xff0c;届时也许需要手动进行迁移。 你可以使用这个模板来开发同时支持桌面、安卓和 iOS 的跨平…

极品三国新手攻略之进阶篇

尊敬的主公大人您好&#xff0c;首先恭喜您在游戏中取得的不俗成绩&#xff0c;相信您已经熟练掌握了不少玩法。今天&#xff0c;我们给大家奉上一份极品三国新手攻略之进阶篇&#xff0c;希望能为您提供有力的帮助。本篇攻略将为您深入分析游戏中武将、装备、试炼塔以及神兵等…

【微服务 SpringCloud】实用篇 · Ribbon负载均衡

微服务&#xff08;4&#xff09; 文章目录 微服务&#xff08;4&#xff09;1. 负载均衡原理2. 源码跟踪1&#xff09;LoadBalancerIntercepor2&#xff09;LoadBalancerClient3&#xff09;负载均衡策略IRule4&#xff09;总结 3. 负载均衡策略3.1 负载均衡策略3.2 自定义负载…

“升级是找死,不升级是等死”,GitLab CE 的痛苦升级之路

编者按&#xff1a;本文转载自公众号运维识堂&#xff0c;已经联系作者取得转载授权。 GitLab 在发展的十余年中&#xff0c;在国内积累了大量的 CE 用户&#xff0c;但是很多 CE 用户并不会跟随 GitLab 的发版节奏&#xff08;月度发版&#xff09;进行版本升级&#xff0c;在…

基于AT89C52+ADC0809+LCD1602的模数转换实验ptoteus仿真设计

一、仿真原理图&#xff1a; 二、仿真效果图&#xff1a; 三、仿真工程&#xff1a; 基于AT89C52ADC0809LCD1602的模数转换实验ptoteus仿真设计资源-CSDN文库

flask实战(问答平台)

问答平台项目结构搭建 先创建一个配置文件config.py&#xff0c;后面有些配置写在这里 #app.py from flask import Flask import configapp Flask(__name__) #绑定配置文件 app.config.from_object(config)app.route(/) def hello_world(): # put applications code herer…

数据结构-----红黑树的删除操作

目录 前言 一、左旋和右旋 左旋&#xff08;Left Rotation&#xff09; 右旋&#xff08;Right Rotation&#xff09; 二、红黑树的查找 三、红黑树的删除 1.删除的是叶子节点 1.1删除节点颜色为红色 1.2删除节点颜色为黑色 1.2-1 要删除节点D为黑色&#xff0c;兄弟节…