C++_智能指针

       

目录

1、内存泄漏

1.1 什么是内存泄漏

1.2 内存泄漏的危害 

1.3 如何避免内存泄漏 

2、智能指针的应用场景 

3、智能指针的原理 

3.1  RAII

3.2 智能指针的使用 

4、智能指针的拷贝问题 

5、auto_ptr

6、unique_ptr

7、share_ptr 

7.1 循环引用 

7.2 weak_ptr

结语 


前言:

        C++的智能指针是普通指针“升级版”,采用了面向对象的思想对普通指针进行了又一层封装,将原本是内置类型的普通指针封装成用户自己定义的一个类对象。智能指针存在的原因:普通指针在进行申请空间时,需要手动释放该指针,有些场景下用户可能来不及释放导致内存泄漏,而一个类对象在销毁时是一定会调用该对象的析构函数的。所以把释放指针的动作交给类的析构函数处理则指针所指向的空间一定会得到释放,这就是智能指针的核心思想

1、内存泄漏

1.1 什么是内存泄漏

         内存泄漏指的是程序员向系统动态申请一块空间,用完之后却没能将其释放掉,最后导致失去了对该空间控制,并且此时系统也无权利用该空间的资源,把这类情况叫做内存泄漏

1.2 内存泄漏的危害 

        若一个程序长期运行并出现了内存泄漏,同样的程序却会让内存不断的消耗殆尽,并且使程序的运行越来越慢,最后当内存都泄漏完了程序就会卡死。 

1.3 如何避免内存泄漏 

        1、养成良好的代码习惯,并且设计程序时严格遵守规范,但凡是申请的空间都记得释放。(但是若遇到了抛异常的情况,哪怕记得释放空间也会造成内存泄漏)

        2、针对抛异常的情况,采用RAII思想或者智能指针来管理内存(下文细讲)。

        3、使用内存泄漏工具来检测程序中是否有内存泄漏。

        因此可以把上述的方法分为两种思路: 1、事先预防型、如上述的1、2点。2、事后查错型,上述的第3点。

2、智能指针的应用场景 

        有些场景下,哪怕程序员已经手动释放了空间,但是依然会造成内存泄漏,比如在进行抛异常时,会出现抛出的异常会连续结束好几个栈帧空间,直到被最外层的栈帧空间所捕获,若之前栈帧空间中还有一些指针没来得及释放该栈帧就结束了,就会造成内存泄漏。

        示例代码如下:

#define _CRT_SECURE_NO_WARNINGS 1

#include<iostream>
using namespace std;

void _func()
{
	int a = -2;
	if (a < 0)
		throw string("a为负数发生错误");
}
void Func()
{
	int* p1 = new int;

	_func();//若_func函数抛异常,则p1会来不及释放,造成内存泄漏

	delete p1;//虽然释放了p1,但仍然可能会造成内存泄漏
}
int main()
{
	try
	{
		Func();
	}
	catch (string& e)
	{
		cout << e << endl;
	}
	return 0;
}

        上述代码的具体示意图如下:

        虽然上述代码的情况可以用重新抛出的问题解决,但是用智能指针的方式显然会更好,智能指针的大概思路是:将p1封装成一个类,再将delete p1写进该类的析构函数,这样一来当结束Func栈帧时p1也会被释放。 

3、智能指针的原理 

        智能指针采用的是RAII的思想,只不过智能指针在此基础上增添了指针的行为,因此需要先了解RAII的具体思想。

3.1  RAII

        RAII全称Resource Acquisition Is Initialization,是利用对象的生命周期来控制程序资源,比如当一个对象的生命周期结束时,因为其析构函数是自动调用的,这时候就可以在该析构函数内实现对资源的管控,达到资源的“自动化”。

        用上述代码来举例RAII思想,先构造一个对象,把p1指向的空间管理权也给到该对象,并且在该对象的析构函数内释放该空间,当该函数栈帧销毁时,该对象会自动的调用析构函数从而释放p1指向的空间,所以无需手动释放p1也不会造成内存泄漏。

        将RAII思想转变为类的代码如下: 

#define _CRT_SECURE_NO_WARNINGS 1

#include<iostream>
using namespace std;

template<class T>
class RAII 
{
public:
	RAII(T* ptr = nullptr)
		: _ptr(ptr)
	{
		cout << "RAII()" << endl;
	}
	~RAII()
	{
		cout << "~RAII()" << endl;
		if (_ptr)
			delete _ptr;
	}

private:
	T* _ptr;
};

void _func()
{
	int a = -2;
	if (a < 0)
		throw string("a为负数发生错误");
}
void Func()
{
	int* p1 = new int(4);
	RAII<int> ra = p1;//ra内的指针_ptr指向和p1同一块空间

	_func();

	delete p1;
}
int main()
{
	try
	{
		Func();
	}
	catch (string& e)
	{
		cout << e << endl;
	}
	return 0;
}

        运行结果:

3.2 智能指针的使用 

        智能指针是在RAII类的原有基础上实现了指针的行为,因为上述代码的RAII并不是一个智能指针,他不具备指针的基本功能,因此需要在上述RAII的基础上加上解引用操作:‘*’,‘->'

        智能指针的基本功能实现:

#define _CRT_SECURE_NO_WARNINGS 1

#include<iostream>
using namespace std;

template<class T>
class SmartPtr//智能指针基本功能
{
public:
	SmartPtr(T* ptr = nullptr)
		: _ptr(ptr)
	{}
	~SmartPtr()
	{
		if (_ptr)
			delete _ptr;
	}
	//解引用功能
	T& operator*() 
	{
		return *_ptr; 
	}
	T* operator->() 
	{
		return _ptr; 
	}
private:
	T* _ptr;
}; 

struct Date//日期类
{
	int _year;
	int _month;
	int _day;
	friend ostream& operator<<(ostream& out,const Date& d);
};
ostream& operator<<(ostream& out,const Date& d)//流提取
{
	cout << d._year<<"."<< d._month << "." << d._day;
	return out;
}

int main()
{
	SmartPtr<int> sp1(new int);
	*sp1 = 12;
	cout << *sp1 << endl;

	SmartPtr<Date> sd(new Date);

	//此处应该是两个->,但是经过编译器优化后变成一个->
	sd->_year = 2024;
	sd->_month = 3;
	sd->_day = 13;

	cout << *sd << endl;//打印日期类
	return 0;
}

        运行结果:

        从代码中可以发现,智能指针实际上就是一个普通指针进行了各种的封装,最后封装成一个类,并且智能指针的大小和普通指针大小是一样的,但是功能却比普通指针更加智能。

4、智能指针的拷贝问题 

         以上虽然已经实现了智能指针,但是有一部分问题还未得到解决,即:指针之间的拷贝。虽然以上的SmartPtr可以实现浅拷贝,但是会导致同一块空间被析构两次,而普通指针的相互拷贝是不会出现这种问题的。

        测试智能指针的拷贝场景:

#define _CRT_SECURE_NO_WARNINGS 1

#include<iostream>
using namespace std;

template<class T>
class SmartPtr//智能指针基本功能
{
public:
	SmartPtr(T* ptr = nullptr)
		: _ptr(ptr)
	{}
	~SmartPtr()
	{
		cout << _ptr << endl;//观察释放的地址
		if (_ptr)
			delete _ptr;
	}
	//解引用功能
	T& operator*()
	{
		return *_ptr;
	}
	T* operator->()
	{
		return _ptr;
	}
private:
	T* _ptr;
};

int main()
{
	SmartPtr<int> sp1(new int);
	SmartPtr<int> sp2(sp1);//浅拷贝
	//sp1和sp2调用各种的析构函数会导致同一块空间被析构两次

	return 0;
}

        运行结果:

        可以从结果看到,程序运行过程中发生了崩溃,并且析构函数释放的是同一块空间。 

5、auto_ptr

        C++标准库中提供了一个名为auto_ptr的智能指针来解决以上问题,该指针的思想是:管理权的转移,即拷贝的对象拥有对该空间的唯一管理权,而被拷贝的对象就失去了对该空间的管理权。

        auto_ptr实现代码:

#define _CRT_SECURE_NO_WARNINGS 1

#include<iostream>
using namespace std;

namespace ZH
{
	template<class T>
	class auto_ptr
	{
	public:
		auto_ptr(T* ptr)
			:_ptr(ptr)
		{}
		//拷贝构造
		auto_ptr(auto_ptr<T>& sp)
			:_ptr(sp._ptr)
		{
			// 被拷贝对象的指针置为空,失去了管理权
			sp._ptr = nullptr;
		}
		//拷贝赋值
		auto_ptr<T>& operator=(auto_ptr<T>& ap)
		{
			if (this != &ap)
			{
				// 释放拷贝对象中的资源
				if (_ptr)
					delete _ptr;
				// 转移管理权
				_ptr = ap._ptr;
				ap._ptr = NULL;
			}
			return *this;
		}
		~auto_ptr()
		{
			if (_ptr)
			{
				cout << "delete:" << _ptr << endl;
				delete _ptr;
			}
		}
		T& operator*()
		{
			return *_ptr;
		}
		T* operator->()
		{
			return _ptr;
		}
	private:
		T* _ptr;
	};
}

int main()
{
	ZH::auto_ptr<int> ap1(new int(12));
	ZH::auto_ptr<int> ap2(ap1);

	//cout << *ap1 << endl;//此时*ap1不再是12,因为ap1的值为空
	cout << *ap2 << endl;
	return 0;
}

        运行结果:

        auto_ptr只是解决了一部分问题,并没有完全解决,因为普通指针即使拷贝给另一个指针,则他们两个都可以访问该空间的数据,并不存在管理权转移的概念,于是C++标准库提出了更加靠谱的unique_ptr指针。

6、unique_ptr

        unique_ptr的思路很简单,就是该类单纯的不支持拷贝构造和拷贝赋值,所以不能直接用unique_ptr类型的对象进行相互的赋值和拷贝,因此不会出现同一块空间被析构两次的情况。

        unique_ptr的实现代码如下:

#define _CRT_SECURE_NO_WARNINGS 1

#include<iostream>
using namespace std;

namespace ZH
{
	template<class T>
	class unique_ptr
	{
	public:
		unique_ptr(T* ptr)
			:_ptr(ptr)
		{}
		~unique_ptr() {
			if (_ptr)
			{
				cout << "delete:" << _ptr << endl;
				delete _ptr;
			}
		}
		T& operator*()
		{
			return *_ptr;
		}
		T* operator->()
		{
			return _ptr;
		}
		/*unique_ptr(const unique_ptr<T>& sp) = delete;
		unique_ptr<T>& operator=(const unique_ptr<T>& sp) = delete;*/
	private:
		T* _ptr;
		unique_ptr(const unique_ptr<T>& sp);
		unique_ptr<T>& operator=(const unique_ptr<T>& sp);
	};
}

int main()
{
	ZH::unique_ptr<int> up1(new int(4));
	ZH::unique_ptr<int> up3(new int(40));
	//ZH::unique_ptr<int> up2(up1);//不支持拷贝构造

	//up3 = up1;//不支持拷贝赋值

	return 0;
}

        运行结果:

        但是有些场景下是需要智能指针进行相互的拷贝和赋值的,unique_ptr是专门处理不需要拷贝的场景,而shared_ptr是既可以支持拷贝和赋值又不会对同一块空间析构两次。

7、share_ptr 

         share_ptr的原理:通过计数的方式来决定管理的空间是否需要被释放,若一块空间的管理者多了一个,那么计数+1,当该空间的管理者被销毁了则不会直接释放该空间(因为还有其他的管理者在管理该空间),而是计数-1,直到计数减为0的时候,即表示该空间只剩最后一个管理者,此时将该空间释放。

        share_ptr的管理逻辑示意图如下:

         share_ptr的代码实现如下:

#define _CRT_SECURE_NO_WARNINGS 1

#include<iostream>
using namespace std;

namespace ZH
{
	template<class T>
	class shared_ptr
	{
	public:
		shared_ptr(T* ptr = nullptr)//构造
			:_ptr(ptr)
			, _pCount(new int(1))
		{}
		shared_ptr(const shared_ptr<T>& sp)//拷贝构造
			:_ptr(sp._ptr)
			, _pCount(sp._pCount)
		{
			++(*_pCount);//新加入管理者则计数+1
		}
		void Release()//资源处理
		{
			if (--(*_pCount) == 0 && _ptr)//计数为0或者_ptr不为空则释放空间
			{
				cout << "delete:" << _ptr << endl;
				delete _ptr;
				delete _pCount;
			}
		}

		shared_ptr<T>& operator=(const shared_ptr<T>& sp)//拷贝故障
		{
			if (_ptr != sp._ptr)
			{
				Release(); //处理被赋值对象原先的资源
				//赋予该对象新的内容
				_ptr = sp._ptr;
				_pCount = sp._pCount;
				++(*_pCount);//新加入管理者则计数+1
			}
			return *this;
		}
		
		~shared_ptr()//析构函数
		{
			Release();
		}
		// 像指针一样使用
		T& operator*()
		{
			return *_ptr;
		}
		T* operator->()
		{
			return _ptr;
		}
		
	private:
		T* _ptr;
		int* _pCount;
	};
}

int main()
{
	//测试拷贝构造
	ZH::shared_ptr<int> sp1(new int(12));
	ZH::shared_ptr<int> sp2(sp1);

	cout << *sp1 << endl;
	cout << *sp2 << endl;
	//测试拷贝赋值
	ZH::shared_ptr<int> sp3(new int(20));
	sp1 = sp3;

	cout << *sp1 << endl;
	cout << *sp3 << endl;

	return 0;
}

        运行结果:

        从结果可以看到,share_ptr可以正常的拷贝和赋值,并且最后只调用了两次析构函数释放不同的空间。 

7.1 循环引用 

        循环引用的问题发生在链表上,链表中的节点有两个智能指针,分别链接前一个节点和后一个节点,具体如下:

struct ListNode//节点
{
 int _data;
 shared_ptr<ListNode> _prev;//注意节点内的成员类型还是share_ptr
 shared_ptr<ListNode> _next;
 ~ListNode(){ cout << "~ListNode()" << endl; }
};
int main()
{
 //创建两个节点指针,并且指向两个节点
 shared_ptr<ListNode> n1(new ListNode);
 shared_ptr<ListNode> n2(new ListNode);

 n1->_next = n2;//单链表链接

 return 0;
}

        以上这种情况的示意图如下:

        若是单链表结构则还是会正常的调用析构函数释空间,不会出问题,具体过程为:

        n2销毁的时候,n2的引用计数从2变为1,此时不会释放掉n2指向的节点,因为n1的_next还指向n2,只有当n1销毁时,才会把n1指向的节点释放(节点的析构函数会再次调用share_ptr的析构函数释放掉_next和_prev指向的空间),n1的计数是1,n1销毁时该计数从1变为0会把n1指向的节点释放,这时候n1节点成员变量也会调用他们的析构函数,即会调用_next的析构函数,因为_next指向n2,因此会把n2引用计数从1变为0,将n2指向的节点给释放掉,至此两个节点全部都被正常释放了。 


        以上单链表的结构不会引发循环引用,若以上n2的_prev指针又指向n1,则此时就会引发循环引用,导致内存泄漏的问题。

        循环引用示意图如下:

        

        这种情况称为闭环,当n1和n2都销毁时,n1和n2指向的节点却不会被释放,因为n2指向的节点被n1节点的成员_next管着,要等n1节点释放了然后调用_next成员的析构函数将n2的计数变为0,n2节点才能释放。而n1节点又被n2节点的_prev管着,n1节点要等n2节点释放了,然后释放_prev指向的节点,即将n1的计数减为0,从而释放n1节点。

        总结就是:1节点等n2节点释放才会释放,而n2节点要等n1节点释放才会释放。若想解决循环引用,则需要用到weak_ptr。

7.2 weak_ptr

         weak_ptr之所以可以解决循环引用,原理就是:当两个节点相互连接时,n1->_next = n2;和n2->_prev = n1;时weak_ptr的_next和 _prev不会增加n1和n2的引用计数。

        测试代码如下:

#define _CRT_SECURE_NO_WARNINGS 1

#include<iostream>
using namespace std;

namespace ZH
{
	template<class T>
	class weak_ptr
	{
	public:
		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;
	};
	struct ListNode//节点
	{
		int _data;
		weak_ptr<ListNode> _prev;//注意节点内的成员类型还是weak_ptr
		weak_ptr<ListNode> _next;
		~ListNode() { cout << "~ListNode()" << endl; }//观察节点释放了几次
	};
};

int main()
{
	//创建两个节点指针,并且指向两个节点
	shared_ptr<ZH::ListNode> n1(new ZH::ListNode);
	shared_ptr<ZH::ListNode> n2(new ZH::ListNode);

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

	return 0;
}

        运行结果:

        从结果可以发现,调用了两次节点的析构函数,说明两个节点都被正确的释放了。

结语 

        以上就是关于智能指针的讲解,智能指针结合了类自动调用析构函数的特性,因此可以实现自动释放空间资源的功能,相比于普通指针显得更加”智能“。最后希望本文可以给你带来更多的收获,如果本文对你起到了帮助,希望可以动动小指头帮忙点赞👍+关注😎+收藏👌!如果有遗漏或者有误的地方欢迎大家在评论区补充,谢谢大家!!   

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

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

相关文章

Linux系统运维命令:查看系统的平均负载(查看CPU的负载)

目 录 一、要求 二、快速了解系统资源利用情况的Linux命令 &#xff08;一&#xff09;cat /proc/loadavg命令 1、命令介绍 2、命令输出 3、命令解释 &#xff08;1&#xff09;前三个数字&#xff1a; &#xff08;2&#xff09;第四个值&#xff1a; &…

鸿蒙Harmony应用开发—ArkTS声明式开发(基础手势:TextClock)

TextClock组件通过文本将当前系统时间显示在设备上。支持不同时区的时间显示&#xff0c;最高精度到秒级。 说明&#xff1a; 该组件从API Version 8开始支持。后续版本如有新增内容&#xff0c;则采用上角标单独标记该内容的起始版本。 子组件 无 接口 TextClock(options?…

c++进阶(c++里的继承)

文章目录 1.继承的概念及定义1.1继承的概念1.2继承的定义1.2.1定义格式1.2.2继承关系和访问限定符1.2.3继承类成员访问方式的变化 2.基类和派生类对象赋值转化3.继承中的作用域4.派生类的默认成员函数5.继承与友元6.继承域静态成员 1.继承的概念及定义 1.1继承的概念 继承机制…

23、设计模式之访问者模式(Visitor)

一、什么是访问者模式 访问者模式是一种行为型设计模式&#xff0c;它可以用于在不修改已有对象结构的情况下&#xff0c;定义新的操作方式。简单地说就是在不改变数据结构的前提下&#xff0c;通过在数据结构中加入一个新的角色——访问者&#xff0c;来达到执行不同操作的目的…

防御安全(IPSec实验)

目录 需求&#xff1a; pc1 ping通 pc2 ,使用IPSec VPN 拓扑图&#xff1a; ​编辑实验配置&#xff1a; 注意&#xff1a; 直接在路由器r1和r2分别配置即可&#xff0c;路由器r1和r2要写一条缺省指向ISP 实验配置截图如下&#xff1a; 2. r1​编辑 3. r3​编辑 3.r…

【C++】—— 代理模式

目录 &#xff08;一&#xff09;什么是代理模式 &#xff08;二&#xff09;为什么使用代理模式 &#xff08;三&#xff09;代理模式实现步奏 &#xff08;四&#xff09;代码示例 &#xff08;五&#xff09;代理模式优缺点 &#xff08;一&#xff09;什么是代理模式 …

车辆路径优化问题(VRP)变体及数学模型

车辆路径优化问题变体及数学模型 一、旅行商问题&#xff08;Travelling salesman problem&#xff0c;TSP&#xff09;TSP问题数学模型TSP问题求解 二、车辆路径问题&#xff08;Vehicle Routing Problem&#xff0c;VRP&#xff09;三、带容量约束的车辆路径优化问题&#xf…

【项目】C++ 基于多设计模式下的同步异步日志系统

前言 一般而言&#xff0c;业务的服务都是周而复始的运行&#xff0c;当程序出现某些问题时&#xff0c;程序员要能够进行快速的修复&#xff0c;而修复的前提是要能够先定位问题。 因此为了能够更快的定位问题&#xff0c;我们可以在程序运行过程中记录一些日志&#xff0c;通…

MySQL8.0 通过data文件恢复数据库

情景&#xff1a; mysql突然访问不了&#xff0c;也启动不了&#xff0c;需要保存之前的数据库文件&#xff0c;在卸载重装恢复数据 步骤&#xff1a; 1、Mysql里的数据一般会自动保存到C:\ProgramData\MySQL\MySQL Server 8.0\Data目录下&#xff0c;卸载前要将其备份。这是…

数据结构之树(Topk问题, 链式二叉树)

一.topk问题 取N个数中最大(小)的前k个值,N远大于k 这道题可以用堆的方法来解决,首先取这N个数的前k个值,用它们建堆 时间复杂度O(k) 之后将剩余的N-k个数据依次与堆顶数据进行比较,如果比堆顶数据大,则将堆顶数据覆盖后向下调整 时间复杂度(N-k)*log(N) 总共的时间复杂度…

【05】消失的数字

hellohello~这里是土土数据结构学习笔记&#x1f973;&#x1f973; &#x1f4a5;个人主页&#xff1a;大耳朵土土垚的博客 &#x1f4a5;所属专栏&#xff1a;C语言函数实现 感谢大家的观看与支持&#x1f339;&#x1f339;&#x1f339; 有问题可以写在评论区或者私信我哦…

缓冲区与C库函数的实现

目录 一、缓冲区 二、C库函数的实现 一、缓冲区 缓冲区本质就是一块内存&#xff0c;而缓冲区存在的意义本质是提高使用者(用户)的效率【把缓冲区理解成寄送包裹的菜鸟驿站】 缓冲区的刷新策略 1. 无缓冲(立即刷新) 2. 行缓冲(行刷新) 3. 全缓冲(缓冲区满了&#xff0c;再刷…

春风吹又生的开源项目「GitHub 热点速览」

随着上周知名 Switch 开源模拟器 Yuzu&#xff08;柚子&#xff09;被任天堂起诉&#xff0c;该项目作者就删库了&#xff0c;但还是要赔偿任天堂数百万美元。此事还在 GitHub 上掀起了一波 Yuzu fork 项目的小浪潮&#xff0c;正所谓野火烧不尽&#xff0c;春风吹又生。 很多读…

Express学习(四)

使用Express写接口 创建基本的服务器 创建API路由模块 编写GET接口 编写POST接口 CORS跨域资源共享 什么是CORS CORS由一系列HTTP响应头组成&#xff0c;这些HTTP响应头决定浏览器是否阻止前端JS代码跨域获取资源。浏览器的同源安全策略默认会阻止网页“跨域”获取资源。但如…

十五、软考-系统架构设计师笔记-面向服务架构设计理论与实践

1、SOA相关概念 面向服务的架构(SOA)的定义 SOA 是一个组件模型&#xff0c;它将应用程序的不同功能单元(称为服务)通过这些服务之间定义良好的接口和契约联系起来。接口是采用中立的方式进行定义的&#xff0c;它应该独立于实现服务的硬件平台、操作系统和编程语言。这使得构…

狂揽Github—start19.7k☆开源OCR—Umi-OCR

文章目录 背景Umi-OCR—源码下载Umi-OCR—可执行程序下载页面介绍截图OCR识别批量OCR识别批量文档二维码全局设置 总结&#xff1a; 背景 大家都知道我是一个Python办公自动化的小小程序员&#xff0c;经常收集一些免费开源的OCR供大家使用&#xff0c;目前我已经写出来多家OCR…

(done) win11 如何安装 Anaconda3 ? 如何安装 jupyter notebook

首先是这个网站 https://www.anaconda.com/download/#windows 下载并安装 anaconda3 进入 anaconda3.navigator 后&#xff0c;会看到如下界面 点击下面这个 Launch 按钮&#xff0c;可以启动 jupyter notebook 如下图&#xff0c;jupyter 出来了

【数据结构七】堆与PriorityQueue详解

堆 在Java中有一种数据结构基于队列&#xff0c;并保证操作的数据带有优先级&#xff0c;该数据结构应该提供了两个最基本的操作&#xff0c;一个是返回最高优先级对象&#xff0c;一个是添加新的对象。这种数据结构就是优先级队列(Priority Queue)。它的底层使用了堆这种数据结…

离散化算法,以Acwing802.区间和为例子(C++实现)

目录 1.例题2.算法实现思路3.代码 1.例题 假定有一个无限长的数轴&#xff0c;数轴上每个坐标上的数都是 0现在&#xff0c;我们首先进行 n 次操作&#xff0c;每次操作将某一位置 x 上的数加 c接下来&#xff0c;进行 m 次询问&#xff0c;每个询问包含两个整数 l 和 r&#…

贪心算法(算法竞赛、蓝桥杯)--奶牛晒衣服

1、B站视频链接&#xff1a;A28 贪心算法 P1843 奶牛晒衣服_哔哩哔哩_bilibili 题目链接&#xff1a;奶牛晒衣服 - 洛谷 #include <bits/stdc.h> using namespace std; priority_queue<int> q;//用大根堆维护湿度的最大值 int n,a,b; int tim,maxn;int main(){s…