C++进阶--C++11--智能指针(重点)

news2025/6/9 11:48:10

文章目录

  • C++进阶--智能指针(重点)
    • 智能指针使用的场景
    • RAII和智能指针的设计思路
    • C++标准库智能指针的使用
    • 定制删除器
    • 智能指针的原理
    • shared_ptr和weak_ptr
      • 循环引用(容易考)
      • weak_ptr
    • 其他
    • 知识扩展(类型转换)
    • 总结
    • 个人学习心得
    • 结语

很高兴和大家见面,给生活加点impetus!!开启今天的编程之路!!
在这里插入图片描述
今天我们进一步c++11中常见的新增表达
作者:٩( ‘ω’ )و260
我的专栏:Linux,C++进阶,C++初阶,数据结构初阶,题海探骊,c语言
欢迎点赞,关注!!

C++进阶–智能指针(重点)

智能指针使用的场景

前面我们学习了异常的相关知识,明白了异常的使用语法,异常的底层原理,学习了异常的一些细节,但是我们还有一种情况,使用异常代码可视化非常低,而且代码也不好看。
来看示例:

void Divide(int a,int b)
{
	if(b == 0)
	{
		throw string("error: Divided by zero!");
	}
	else {
		cout << double(a)/double(b) << endl;
	}
}
int main()
{
	int arr1 = new int[10];
	int arr2 = new int[10];
	
	try{
		int a,b;cin >> a >> b;
		Divide(a,b);
	}
	catch(...)//捕获任意类型的错误
	{
		//相关操作,释放arr1,arr2
	}
	return 0;
}

我们下方写catch(…)的目的就是为了来释放前面申请到的arr1和arr2资源。
但是new本身可能抛异常啊,如果说此时申请arr1抛异常,无伤大雅,因为资源还没有申请下来,但是如果是申请arr2抛异常,那么我的arr1肯定是已经申请下来了,不然走不到申请arr2资源的这句代码中去。
所以:如果使用异常解决这个问题的话,需要在申请arr2资源的时候再来套一个try,catch,缺点就是代码可视化降低

我们来尝试使用智能指针解决这个问题

RAII和智能指针的设计思路

首先:这里的问题就是需要析构的东西抛异常可能会导致无法执行正确的析构,进而导致内存泄漏。
如果说我们将申请到的资源托管给一个对象,那就可以解决这个问题,为什么呢?因为对象不论是局部还是全局的,函数执行结束或者代码执行结束这个对象会调用自己的析构,即:对象定义和销毁的时候构造函数和析构函数是不用用户自己调用的,编译器自己帮我们调用

RALL(资源申请立即初始化):本质是⼀种利用对象生命周期来管理获取到的动态资源,避免内存泄漏。RAII在获取资源时把资源委托给一个对象,接着控制对资源的访问,资源在对象的生命周期内始终保持有效,最后在对象析构的时候释放资源。

智能指针除了需要满足RALL思想,还需要设计一系列接口还管理这个资源

我们可以写一个简单的项目来描述智能指针需要设计的内容:

namespace Mrzeng
{
	template<class T>//可能接收到各种类型的指针
	class Smart_ptr
	{
	public:
		Smart_ptr(T* ptr)
			:_ptr(ptr)
		{}
		~Smart_ptr()
		{
			delete _ptr;//对象调用析构的时候将申请到的资源释放掉
		}
		//一系列简单接口
		T& operator*()
		{
			return *_ptr;
		}
		T* operator->()
		{
			return _ptr;
		}
		T* get()
		{
			return _ptr;
		}
	public:
		T* _ptr;
	}
}

反正记住:operator->一定是返回一个地址,因为到时候使用的时候实际上有两个->,前者是operator->(),后者单纯是->操作符。->操作符对指针操作,所以operator->()必须返回一个地址。

学习了RALL思想,上述代码应该这样修改

int main()
{
	Mrzeng::Smart_ptr<int> sp1(new int[10]);//这种情况下,不会存在内存泄漏的问题
	Mrzeng::Smart_ptr<int> sp2(new int[10]);//这种情况下,不会存在内存泄漏的问题
	
	try{
		int a,b;cin >> a >> b;
		Divide(a,b);
	}
	catch(...)//捕获任意类型的错误
	{
		//相关操作,释放arr1,arr2
	}
	return 0;
}

C++标准库智能指针的使用

头文件:< memory >
智能指针主要分为两种,在此之前,我们先介绍c++98中的智能指针。
auto_ptr:

首先先说明结论:auto_ptr在很多企业中的使用条例被禁止使用,原因为底层实际上是管理权转移

我们主要使用日期类来演示,来看代码:

class Date
{
public:
	Date(int year, int month, int day)
		:_year(year), _month(month), _day(day)
	{}
	~Date()
	{
		cout << "~Date" << endl;
	}
private:
	int _year = 1;
	int _month = 1;
	int _day = 1;
};

int main()
{
	auto_ptr<Date> ap1(new Date(2025, 6, 8));
	auto_ptr<Date> ap2(ap1);//使用ap1拷贝ap2

	cout << ap2.get() << endl;
	cout << ap1.get() << endl;
	return 0;
}

在这里插入图片描述
我们发现,这个时候ap1(被拷贝对象)资源全部清空了,本质上就是ap2(拷贝对象)拷贝之后内部重置了ap1,造成ap1的悬空
正是因为这个原因,而且ap1的悬空还是内部自己实现的,造成很多程序员书写的时候可能不会注意到这点。

c++11中的智能指针:unique_ptr和shared_ptr
unique_ptr:特点是只能够对单一资源进行管理,即1对1,。所以内部没有实现拷贝构造,只有移动构造,移动构造是对资源进行转移,肯定能够保证1对1的规则
在这里插入图片描述
在这里插入图片描述

我们发现:为什么unique_ptr和auto_ptr效果一样,可后者被禁用了呢?因为后者是底层搞得,上层使用者没学习的话根本不知道,但是前后调用移动构造,学习了右值引用章节知识肯定知道也不会再对资源进行访问

shared_ptr:特点是可以多个shared_ptr管理一个资源,即多对1,也可以一个shared_ptr管理一个资源,即1对1。内部实现了拷贝构造。既然可以由多个对象管理一块资源,为了防止一块资源被delete多次(同一块资源被delete多次,直接报错),使用了引用计数的方法引用计数的目的就是防止同一块空间被析构多次

在这里插入图片描述

shared_ptr和unique_ptr区别:前者可以拷贝构造,后者不能够拷贝构造。

思考:shared_ptr能够管理一块资源,即1对1,unique_ptr也能够管理一块资源,即1对1,为什么还要有unique_ptr呢?

shared_ptr引用计数底层有性能消耗,所以如果管理对象一不涉及拷贝,更适合使用unique_ptr。反之更适合使用shared_ptr。

c++第三个智能指针:weak_ptr
特点:不符合RALL规则,所以不能够来使用管理对象。weak_ptr的产生是为了解决shared_ptr循环引用的问题,这点我们稍后讲解

定制删除器

我们再来看,前面我们举例都是使用delete删除资源的,确实,在标准库中,智能指针的默认析构资源使用的是delete,如果我是申请的数组呢?是不是需要使用delete [ ]呢?但是后面c++更新中模版特化了这种情况
在这里插入图片描述
那如果申请的资源是fopen使用的文件的,释放这个资源应该使用fclose而非delete。
为了处理这种情况,需要定制删除器。
删除器的本质就是一个可调用对象

在这里插入图片描述
这里设计的复杂了,同样都是只能指针,但是传递删除器的方式却不相同。

先来看如何使用:
shared_ptr:
因为是函数模版,所以需要在构造shared_ptr的时候传递一个可调用对象过去

struct FClose 
{
	void operator()(FILE* ptr)
	{
		cout << "fclose:ptt" << endl;
		fclose(ptr);
	}
};

int main()
{
	//使用仿函数执行删除器
	shared_ptr<FILE> sp1(fopen("Test1.cpp", "r"),FClose());//需要传一个对象过去,是传给的函数模版,函数指针和仿函数相差不大,这里不再演示
	shared_ptr<FILE> sp2(fopen("Test2.cpp", "r"), [](FILE* ptr) {//传lambda表达式过去
		cout << "fclose:ptr" << endl;
		fclose(ptr);
		});
	return 0;
}

unique_ptr
因为删除器是作为模版参数传过去的,所以传递的应该是一个类型

struct FClose 
{
	void operator()(FILE* ptr)
	{
		cout << "fclose:ptt" << endl;
		fclose(ptr);
	}
};

int main()
{
	//使用仿函数执行删除器
	unique_ptr<FILE,FClose> sp1(fopen("Test1.cpp", "r"));//unique_ptr删除器是在模版位置传递的,所以传一个类型,函数指针与仿函数相差不大,不在演示
	//演示传递lambda表达式,比较复杂,先来看结果
	auto FClose = [](FILE* ptr) {
		cout << "fclose:ptr" << endl;
		fclose(ptr);
		};
	unique_ptr<FILE, decltype(FClose)> sp2(fopen("Test1.cpp", "r"),FClose);
	return 0;
}

解释:decltype(类型推导关键字):在编译时进行类型推导
前面已经提到,unique_ptr的定制删除器需要使用类型传递给模版,但是lambda表达式是一个匿名函数对象,我传递的需要是类型,所以使用decltype关键字。
其次:后面为什么要传一个FClose呢?
因为lambda表达式无法实例化出对象,需要使用拷贝构造拷贝出一个对象,调用这个对象,执行完删除资源的操作之后就需要将这个对象析构。不可能我去调用一个类型吧。
其实,调用了这个unique_ptr中的构造函数的这个接口:

在这里插入图片描述
记住:如果头铁就要使用unique_ptr来定制删除器,如果是lambda表达式作为模版参数的话,记得构造时添加一个可调用对象,左值或右值都可以

总结:定制删除器尽量都是用shared_ptr,而且尽量都是用lambda表达式

细节:上述定制删除器unique_ptr因为传的是模版,所以在整个类中都是可以直接使用这个对象的。但是,shared_ptr中发现,定制删除器是作为一个函数模版有函数模版推导而来的。

那么我们如果在需要析构资源的时候使用到这个定制删除器呢?
使用包装器,目的是将删除器保存下来,想用的时候就用,而不限于只有构造函数能够用这个删除器。而且,最好包装器需要给一个缺省值走初始化列表,因为包装器(这里使用function)的默认构造是空的,但是去取的话,如果包装器是空,就会抛异常,这里跟库一样,将delete设置为缺省值,还要注意包装器的类型,因为什么事情也不干,返回值是void,传递的参数是T*

智能指针的原理

1:首先需要包含定制删除器的细节

2:析构函数的细节:
只有当引用计数为1的时候,我们才需要析构这块资源,否则只是引用计数减少1

3:拷贝构造的细节:
前面我们提到unique_ptr不支持拷贝构造,只支持移动构造,所以,如果我们创建一个对象并初始化调用构造函数只能够这样写:

shared_ptr<Date> sp1(new Date(2025,6,8));//能够这样写
shared_ptr<Date> sp1 = {2025,6,8};//不能够这样写。

想一下问什么不能够这样写呢?
这需要了解类型转换相关的知识,这点我会在后面讲解。
首先我们使用sp1来管理资源为{2025,6,8}的对象。

会执行的操作如下:构造+拷贝构造(会被直接优化成直接构造)

问题:构造+拷贝构造优化为直接构造在c++11中c++委员会规定还没有规定!即有些编译器优化了,有些编译器没有优化。但是unique_ptr是不支持拷贝构造的,即有些编译器会出错,有些编译器可能不会报错!
为了杜绝这种情况发生,使用了explicit修饰,目的是:避免类的单参数构造函数被用于隐式类型转换

在这里插入图片描述
其实这里还有一种写法,使用make_shared+传递参数返回一个shared_ptr,作用是:它能够减少代码冗余,用户不用显式地书写 new 表达式,局限是:没办法自定义删除器(deleter)。如果确实需要自定义删除器,那就得直接使用 new 来创建 std::shared_ptr(依靠构造函数)

4:赋值重载细节:注意防止地址重复赋值和相同地址的赋值
来看案例:sp1 = sp2
将sp2赋值给sp1,需要考虑sp1的引用计数是否为1,如果为1,则直接析构,然后修改为sp2,如果sp1的引用计数不为1,只用修改sp1管理资源的引用计数。然后修改成sp2,最后sp2的引用计数需要加1,因为多了一个sp1来管sp2管的资源了。

而且:如果说sp1和sp2地址相同,就啥也不做,做了也是白做。

最后代码部分已经绑定到这篇文章了。

shared_ptr和weak_ptr

循环引用(容易考)

shared_ptr看似非常完美,既支持拷贝构造,也能多个shared_ptr管理一个资源。但是还有不足,来看下面案例:

如果此时需要创建一个链表,这个链表中只有两个结点,这两个结点相互指向,同时,我还需要智能指针来管理这两个结点。

来看代码:

struct ListNode
{
	int _data;
	/*ListNode* _next;
	ListNode* _prev;*/
	std::shared_ptr<ListNode> _next;
	std::shared_ptr<ListNode> _prev;
	~ListNode()
	{
		cout << "~ListNode()" << endl;
	}
};
int main()
{
	std::shared_ptr<ListNode> n1(new ListNode);
	std::shared_ptr<ListNode> n2(new ListNode);
	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;
	return 0;
}

先说明一下为什么_next指针的类型是这个,下方我们使用了share_ptr来管理结点,可以理解为此时这个结点的类型是一个智能指针了,只不过真正的结点被这个share_ptr管理着。所以此时_next和_prev都给换了一个类型。不换的话是会报错的-类型不匹配。
在这里插入图片描述
结果:
在这里插入图片描述
代码执行结束,管理的对象销毁,但是,并没有调用析构函数,违背了RALL规则,为什么呢?
来看图示:
在这里插入图片描述
先明白,为什么引用计数变成2了?
因为_next和_prev指针都是一个智能指针,管理一个资源之后这个资源引用计数肯定需要加1

循环引用的逻辑:左边结点引用计数为1,想要销毁,必须让管理左结点的对象prev销毁,对象prev在右边结点中,右边结点销毁,其中的内容销毁,右边结点想要销毁,必须让管理右边结点的对象next销毁,但是next在左边结点,左边结点销毁,其中的内容销毁,要想让左边结点销毁,必须让管理左边结点的对象prev销毁。我们发现,是不是回到了第一句!!即左结点想要销毁,左结点才能够销毁!!

并且,如果不是相互指向,都不会右循环引用,即单个方向指向不存在循环引用。

再来想一下是什么原因造成这一个现象的?
原因是不是next和prev指针增加了对应资源的引用计数!!
所以,为了不增加对应资源的引用计数,同时还要链接两个节点,weak_ptr运应而生。

weak_ptr不遵循RALL规则,不管理资源,也就不会增加引用计数
上述代码只用将_next和_prev的类型修改为weak_ptr< ListNode >,就能够杜绝循环引用的现象

来看结果:
在这里插入图片描述
析构正常执行!!

weak_ptr

weak_ptr主要解决shared_ptr的循环引用的问题,不遵循RALL,不会增加引用计数,相应的,其中没有operator*和operator->等接口,因为weak_ptr不管理资源。因为如果weak_ptr绑定的shared_ptr已经释放了资源,那么weak_ptr再去访问资源十分危险。

weak_ptr支持expired检查指向的资源是否过期,use_count也可获取shared_ptr的引用计数,weak_ptr想访问资源时,可以调用lock返回一个管理资源的shared_ptr,如果资源已经被释放,返回的shared_ptr是一个空对象,如果资源没有释放,则通过返回的shared_ptr访问资源是安全的。

是否过期的判断条件 :shared_ptr对象管理的资源,如果这些对象都不管理这个资源了,就过期,反之只要还有一个对象管理着这个资源,就没有过期,呈现的结果为为真即过期,为假不过期。

来看示例代码:

int main()
{
	std::shared_ptr<string> sp1(new string("111111"));
	std::shared_ptr<string> sp2(sp1);
	std::weak_ptr<string> wp = sp1;
	cout << wp.expired() << endl;
	cout << wp.use_count() << endl;
	// sp1和sp2都指向了其他资源,则weak_ptr就过期了
	sp1 = make_shared<string>("222222");
	cout << wp.expired() << endl;
	cout << wp.use_count() << endl;

	sp2 = make_shared<string>("333333");
	cout << wp.expired() << endl;
	cout << wp.use_count() << endl;

	wp = sp1;
	//std::shared_ptr<string> sp3 = wp.lock();
	auto sp3 = wp.lock();//会返回一个shared_ptr
	cout << wp.expired() << endl;
	cout << wp.use_count() << endl;
	*sp3 += "###";

	cout << *sp1 << endl;

	return 0;
}

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

其他

剩下的还有线程安全问题,之后我们再讲解。

知识扩展(类型转换)

上面我们提到了类型转换,我们可以浅浅讲解一点。
这里直接引出结论:

其他类型a(自定义类型,内置类型)转换成自定义类型b:支持自定义类型的构造函数可以使用a作为参数,然后分为单参数和多参数的区别。
自定义类型a转换成内置类型:使用重载函数,operator 内置类型(参数),返回类型为该内置类型

为什么是这样呢?首先强制类型转换操作符是(),如int a = 1;(double)a;
但是()已经被仿函数占用了,所以只能够定义成如此了。

来看示例:
场景:判断sp1是否管理了资源,可以使用

if(sp1)
{
	//操作
}
//等价于
if(sp1.operator bool())
{
	//操作
}

在这里插入图片描述
当然在流插入中也有operator bool()也有所涉及,使用场景如下

while(cin >> x)
{
	//...
}

总结

今天我们学习了智能指针,分析了使用智能指针的场景,智能指针满足的规则,底层,定制删除器(语法,总结规律,什么场景下需要使用),循环引用(重点),以及如何来手撕unique_ptr,shared_ptr,weak_ptr这三种智能指针和这三类智能指针的特点区别,学习了类型转换和一些细节。
这些知识点记得复习哦

个人学习心得

在c++11之后,走隐式类型转换的途径一般都是通过给的形参形成一个initializer_list,看这个initializer_list能不能来初始化我想形成的类。
例如有一个日期类:

shared_ptr<Date> sp1 = {2025,6,8};//不能够这样写。

如果说此时我的日期类里面书写了initializer_list这个构造方式,就能够来构造一个Date,并且此时拷贝构造没有explicit关键字修饰(但是库中是有这个修饰的哈),就能够转换为一个Date*

结语

感谢大家阅读我的博客,不足之处欢迎留言指出,感谢大家支持!!
路漫漫其修远兮,吾将上下而求索
在这里插入图片描述

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

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

相关文章

企业私有化部署DeepSeek实战指南:从硬件选型到安全运维——基于国产大模型的安全可控落地实践

一、部署前的战略评估与规划 私有化部署不仅是技术工程&#xff0c;更是企业数据战略的核心环节。需重点评估三方面&#xff1a; 1、业务场景适配性​ 适用场景&#xff1a;金融风控&#xff08;需实时数据处理&#xff09;、医疗诊断&#xff08;敏感病历保护&#xff09;、政…

【西门子杯工业嵌入式-5-串口实现数据收发】

西门子杯工业嵌入式-5-串口实现数据收发 一、通信基础1.1 什么是通信1.2 嵌入式系统中的通信 二、串行通信原理2.1 串行通信简介2.2 通信参数约定 三、GD32F470 串口资源与性能3.1 串口硬件资源 四、串口通信的实现4.1 串口初始化流程4.2 串口发送函数编写4.3 使用 printf 实现…

深度学习登上Nature子刊!特征选择创新思路

2025深度学习发论文&模型涨点之——特征选择 特征选择作为机器学习与数据挖掘领域的核心预处理步骤&#xff0c;其重要性在当今高维数据时代日益凸显。 通过识别最具判别性的特征子集&#xff0c;特征选择算法能够有效缓解"维度灾难"、提升模型泛化能力&#x…

javaSE复习(7)

1.KMP算法 使用KMP算法在主串 "abaabaabcabaabc" 中搜索模式串 "abaabc"&#xff0c;到匹配成功时为止&#xff0c;请问在匹配过程中进行的单个字符间的比较次数是&#xff08;&#xff09;。 10次 用于互斥时 初值为1 在一个并发编程环境中&#xff0c…

WireShark相关技巧

文章目录 1 Wireshark如何设置解析SIP 1 Wireshark如何设置解析SIP 编辑->首选项->protocols->sip 选中sip 2 点击“编辑”->“首选项”->“protocol”->ESP ,按照如下红框显示&#xff0c;进行勾选&#xff0c;点击应用

DAY 45 Tensorboard使用介绍

知识点回顾&#xff1a; tensorboard的发展历史和原理tensorboard的常见操作tensorboard在cifar上的实战&#xff1a;MLP和CNN模型 作业&#xff1a;对resnet18在cifar10上采用微调策略下&#xff0c;用tensorboard监控训练过程。 PS: tensorboard和torch版本存在一定的不兼容…

台式机电脑CPU天梯图2025年6月份更新:CPU选购指南及推荐

组装电脑选硬件的过程中,CPU的选择无疑是最关键的,因为它是最核心的硬件,关乎着一台电脑的性能好坏。对于小白来说,CPU天梯图方便直接判断两款CPU性能高低,准确的说,是多核性能。下面给大家分享一下台式机电脑CPU天梯图2025年6月版,来看看吧。 桌面CPU性能排行榜2025 台…

将单体架构项目拆分成微服务时的两种工程结构

一.独立Project 1.示意图 此时我们创建一个文件夹&#xff0c;在这个文件夹中&#xff0c;创建N个Project&#xff0c;每一个Project对应一个微服务&#xff0c;组成我们的最终的项目。 2.特点 适合那种超大型项目&#xff0c;比如淘宝&#xff0c;但管理负担比较重。 二.Mave…

Unity3D 开发中的创新技术:解锁 3D 开发的新境界

在 3D 开发的广袤天地里&#xff0c;Unity3D 一直是众多开发者的得力伙伴。可如今&#xff0c;普通的开发方式似乎难以满足日益增长的创意与效率需求。你是否好奇&#xff0c;凭什么别家团队能用 Unity3D 打造出令人拍案叫绝的 3D 作品&#xff0c;自己却总感觉差了那么一点火候…

UOS 20 Pro为国际版WPS设置中文菜单

UOS 20 Pro为国际版WPS设置中文菜单 查看UOS操作系统系统安装国际版wps并汉化方法1:下载zh_CN.tar.gz语言包方法2&#xff1a;手动从国内版wps12的包中提取中文菜单解压国内版wps的包 复制中文语言包到wps国际版目录下安装Windows字体 安装开源office 查看UOS操作系统系统 # 查…

单例模式与锁(死锁)

目录 线程安全的单例模式 什么是单例模式 单例模式的特点 饿汉实现方式和懒汉实现方式 饿汉⽅式实现单例模式 懒汉⽅式实现单例模式 懒汉⽅式实现单例模式(线程安全版本) 单例式线程池 ThreadPool.hpp threadpool.cc 运行结果 线程安全和重⼊问题 常⻅锁概念 死…

理解世界如淦泽,穿透黑幕需老谋

理解世界如淦泽&#xff0c;穿透黑幕需老谋 卡西莫多 2025年06月07日 安徽 极少主动跟别人提及恩师的名字&#xff0c;生怕自己比孙猴子不成器但又比它更能惹事的德行&#xff0c;使得老师跟着被拖累而脸上无光。不过老师没有象菩提祖师训诫孙猴子那样不能说出师傅的名字&a…

第三讲 Linux进程概念

1. 冯诺依曼体系结构 我们买了笔记本电脑, 里面是有很多硬件组成的, 比如硬盘, 显示器, 内存, 主板... 这些硬件不是随便放在一起就行的, 而是按照一定的结构进行组装起来的, 而具体的组装结构, 一般就是冯诺依曼体系结构 1.1. 计算机的一般工作逻辑 我们都知道, 计算机的逻…

stm32-c8t6实现语音识别(LD3320)

目录 LD3320介绍&#xff1a; 功能引脚 主要特色功能 通信协议 端口信息 开发流程 stm32c8t6代码 LD3320驱动代码&#xff1a; LD3320介绍&#xff1a; 内置单声道mono 16-bit A/D 模数转换内置双声道stereo 16-bit D/A 数模转换内置 20mW 双声道耳机放大器输出内置 5…

爬虫学习记录day1

什么是逆向&#xff1f; 数据加密 参数加密 表单加密扣js改写Python举例子 4.1 元素&#xff1a;被渲染的数据资源 动态数据 静态数据 如果数据是加密的情况则无法直接得到数据 4.2 控制台&#xff1a;输出界面 4.3 源代码页面 4.4 网络&#xff1a;抓包功能&#xff0c;获取浏…

agent基础概念

agent是什么 我个人认为agent并没有一个所谓完美的定义,它是一个比较活的概念,就像是你眼中的一个机器人你希望它做什么事,和我眼中的机器人它解决事情的流程,其实是可以完全不同的,没有必要非得搞一个统一的概念或流程来概况它。但我们依然可以概况几个通用的词来描述它…

让音乐“看得见”:使用 HTML + JavaScript 实现酷炫的音频可视化播放器

在这个数字时代,音乐不仅是听觉的享受,更可以成为视觉的盛宴!本文用 HTML + JavaScript 实现了一个音频可视化播放器,它不仅能播放本地音乐、控制进度和音量,还能通过 Canvas 绘制炫酷的音频频谱图,让你“听见色彩,看见旋律”。 效果演示 核心功能 本项目主要包含以下…

CAD实体对象智能识别

CAD实体对象智能识别 概述 实体对象智能识别能够在CAD图纸中智能识别和匹配相似的实体对象。该系统采用模式匹配算法&#xff0c;支持几何变换&#xff08;缩放、旋转&#xff09;&#xff0c;并提供了丰富的配置选项和可视化界面。 系统提供两种主要的识别方式&#xff1a;…

LabVIEW音频测试分析

LabVIEW通过读取指定WAV 文件&#xff0c;实现对音频信号的播放、多维度测量分析功能&#xff0c;为音频设备研发、声学研究及质量检测提供专业工具支持。 主要功能 文件读取与播放&#xff1a;支持持续读取示例数据文件夹内的 WAV 文件&#xff0c;可实时播放音频以监听被测信…

RoseMirrorHA 双机热备全解析

在数字化时代&#xff0c;企业核心业务系统一旦瘫痪&#xff0c;每分钟可能造成数万甚至数十万的损失。想象一下&#xff0c;如果银行的交易系统突然中断&#xff0c;或者医院的挂号系统无法访问&#xff0c;会引发怎样的连锁反应&#xff1f;为了守护这些关键业务&#xff0c;…