C++ ---智能指针详解

news2024/11/17 5:40:46

文章目录

  • 前言
  • 一、 为什么需要智能指针?
  • 二、内存泄漏
    • 2.1 什么是内存泄露?危害是什么?
    • 2.2 内存泄露的分类
    • 2.3 如何避免内存泄露
  • 三、智能指针的使用及原理
    • 3.1 RAII
    • 3.2 智能指针的原理
    • 3.3 std::autoptr
    • 3.4 std::unique_ptr
    • 3.5 std::shared_ptr
      • std::shared_ptr的循环引用问题
    • 3.6 std::weak_ptr
  • 四、定制删除器
  • 五、完整代码
  • 六、C+11和boost中智能指针的关系
  • 总结


前言

在这里插入图片描述


正文开始

一、 为什么需要智能指针?

首先我们来分析下面的程序有没有什么关于内存方面的问题呢?
注意:分析func函数中的问题

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

问题分析:上面的问题分析出来我们发现有什么问题?

  1. 在这里抛异常会在main函数中捕捉,不会导致问题.
  2. 在这里抛异常会在main函数中捕捉,但是new p1的空间没有释放,导致内存泄露!
  3. 调用div函数后抛异常会在main函数中捕捉,但是new p1和p2的空间没有释放,导致内存泄露!

二、内存泄漏

2.1 什么是内存泄露?危害是什么?

内存泄漏: 内存泄露指因为疏忽或错误造成程序未能释放已经不再使用的内存情况.内存泄露并不是指内存在物理上的消失,而是应用程序分配某段内存后,因为设计错误,失去了对该段内存的控制,因而造成了内存的浪费.
内存泄露的危害: 长期运行的程序出现内存泄露,影响很大,如操作系统、后台服务等等,出现内存泄露会导致响应越来越慢,最终卡死.

int div()
{
	int a, b;
	cin >> a >> b;
	if (b == 0)
		throw invalid_argument("除0错误");
	return a / b;
}
void func()
{
	// 1.内存申请了忘记释放
	int* p1 = (int*)malloc(sizeof(int));
	int* p2 = new int;

	// 2.异常安全问题
	int* p3 = new int[10];

	div(); // 这里div函数抛异常导致delete[] p3 未执行,p3没有被释放
	
	delete[] p3;
}

2.2 内存泄露的分类

C/C++程序中一般关心两种方面的内存泄露:

  • 堆内存泄露(Heap Leak)
    堆内存是指程序执行中依据须要分配通过malloc/calloc/realloc/new等从堆中分配的一块内存,用完之后必须通过调用相应的free或者delete删掉.假设程序的设计错误导致这部分内存没有被释放,那么以后这部分空间将无法再被使用,就会产生Heap Leak.
  • 系统资源泄露
    指程序使用系统分配的资源,比方套接字、文件描述符、管道等没有使用对应的函数释放掉,导致系统资源的浪费,严重可导致系统效能减少,系统执行不稳定.

2.3 如何避免内存泄露

  1. 工程前期良好的设计规范,养成良好的编码规范,申请的内存空间记着匹配的去释放.ps:这个理想状态.但是如果碰上异常时,就注意释放了,还是可能会出问题.需要下一条智能指针来管理才有保证.
  2. 采用PAII思想或者智能指针来管理资源.
  3. 有些公司内部规范使用内部实现的私有内存管理库.这套库自带内存泄露检测的功能选项.

总结:内存泄漏非常常见,解决方案分为两种: 1.事前预防型.如智能指针等. 2. 事后查错型.如泄露检测工具等.

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

3.1 RAII

RAII(Resource Acquisition Is Initialization)是一种利用对象生命周期来控制程序资源(如内存、文字句柄、网络连接、互斥量等等)简单技术.

在对象构造时获取资源,接着控制对资源的访问使之在对象的生命周期内始终保持有效,**最后在对象析构的时候释放资源.**借此,实际上把管理一份资源的责任托管给一个对象.

这种做法有两大好处:

  • 不需要显式地释放资源.
  • 采用这种方式,对象所需的资源在其生命周期内始终保持有效.
//使用RAII的思想设计的SmartPtr类
namespace hulu
{
	template<class T>
	class SmartPtr
	{
	public:
		SmartPtr(T* ptr)
			:_ptr(ptr)
		{}
		~SmartPtr()
		{
			cout << "delete[]" << _ptr << endl;
			delete[] _ptr;
		}
	private:
		T* _ptr;
	};
}
double div()
{
	double a, b;
	cin >> a >> b;
	if (b == 0)
		throw invalid_argument("除0错误");
	return a / b;
}
void Func()
{
	hulu::SmartPtr<int> sp1(new int[10]);
	hulu::SmartPtr<int> sp2(new int[10]);
	hulu::SmartPtr<int> sp3(new int[10]);
	hulu::SmartPtr<int> sp4(new int[10]);
	div();

}
int main()
{
	try {
		Func();
	}
	catch (const exception& e)
	{
		cout << e.what() << endl;
	}
	return 0;
}

不抛出异常的情况

在这里插入图片描述

抛出异常的情况

在这里插入图片描述

3.2 智能指针的原理

上述的SmartPtr还不能称其为智能指针,因为他还不具有指针的行为,指针可以解引用,也可以通过->去访问所指空间中的内容,因此: auto_ptr模板类中还需要将、->重载下,才可让其像指针一样去使用.*

namespace hulu
{
	template<class T>
	class SmartPtr
	{
	public:
		SmartPtr(T* ptr)
			:_ptr(ptr)
		{}
		~SmartPtr()
		{
			cout << "delete[]" << _ptr << endl;
			//delete[] _ptr;
			delete _ptr;
		}

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

	private:
		T* _ptr;
	};
}

总结智能指针的原理:

  1. RAII特性
  2. 重载operator*和operator->,具有像指针一样的行为!

3.3 std::autoptr

auto_ptr的文档介绍
C++98版本的库中就提供了auto_ptr的智能指针.下面演示的auto_ptr的使用及问题.

auto_ptr的实现原理:管理权转移的思想,下面模拟实现了一份hulu::auto_ptr来了解的它的原理.

namespace hulu
{
	template<class T>
	class auto_ptr
	{
	public:
		auto_ptr() {}
		auto_ptr(T* ptr)
			:_ptr(ptr)
		{
			ptr = nullptr;
		}
		auto_ptr(auto_ptr<T>& sp)
			:_ptr(sp._ptr)
		{
			sp._ptr = nullptr;
		}

		auto_ptr<T>& operator=(auto_ptr<T>& sp)
		{
			//检测是不是自己给自己赋值
			if (this != &sp)
			{
				//释放当前对象的资源
				if (_ptr)
					delete _ptr;
				//转移sp中的资源到当前对象
				_ptr = sp._ptr;
				sp._ptr = nullptr;
			}
			return *this;
		}

		~auto_ptr()
		{
			if (_ptr)
			{
				cout << "delete[]" << _ptr << endl;
				delete _ptr;
				_ptr = nullptr;
			}

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

	private:
		T* _ptr;
	};
}
//C++98 auto_ptr 管理权转移,被拷贝的对象悬空 
int main()
{
	//std::auto_ptr<int> sp1(new int);
	//std::auto_ptr<int> sp2 = sp1;
	 
	hulu::auto_ptr<int> sp1(new int);
	hulu::auto_ptr<int> sp2 = sp1;
	hulu::auto_ptr<int> sp3;
	sp3 = sp1;

	// sp1悬空
	//*sp1 = 10;
	*sp2 = 20;
	return 0;
}

在这里插入图片描述
在这里插入图片描述

3.4 std::unique_ptr

C++11中开始提供更靠谱的unique_ptr

unique_ptr的使用文档

unqiue_ptr的实现原理:简单粗暴的防拷贝,下面简单模拟实现了一份unique_ptr来了解它的原理.

namespace hulu{
	template<class T>
	class unique_ptr
	{
	public:
		unique_ptr(T* ptr)
			:_ptr(ptr)
		{}
		~unique_ptr()
		{
			if (_ptr)
			{
				cout << "delete[]" << _ptr << endl;
				delete _ptr;
				_ptr = nullptr;
			}
		}
		//像指针一样
		T& operator*()
		{
			return *_ptr;
		}
		T* operator->()
		{
			return _ptr;
		}

	private:
		unique_ptr(unique_ptr<T>& sp)= delete;
		unique_ptr<T>& operator=(const unique_ptr<T>& sp)= delete;
	private:
		T* _ptr;
	};
}

在这里插入图片描述

3.5 std::shared_ptr

C++11中开始提供更靠谱的并且支持拷贝的shared_ptr

share_ptr的文档介绍

**shared_ptr的原理:是通过引用计数的方式来实现多个shared_ptr对象之间共享资源.**例如:上课开始前,最后一个进入教室的学生需要把门锁着.

  1. shared_ptr在其内部,给每个资源都维护了一份计数,用来记录该份资源被几个对象共享.
  2. 对象被销毁时(也就是析构函数调用),就说明自己不使用该资源了,对象的引用计数减一
  3. 如果引用计数是0,就说明自己是最后一个使用该资源的对象,必须释放该资源.
  4. 如果不是0就说明除了自己还有其他对象在使用该份资源,不能释放该资源,否则其他对象就成野指针了.
// 引用计数支持多个拷贝管理同一个资源,最后一个析构对象释放资源
namespace hulu
{
	template<class T>
	class shared_ptr
	{
	public:
		shared_ptr(T* ptr)
			:_ptr(ptr),
			_pCount(new int(1))
		{}
		~shared_ptr()
		{
			Release();
		}
		shared_ptr(const shared_ptr<T>& sp)
			:_ptr(sp._ptr),
			_pCount(sp._pCount)
		{
			(*_pCount)++;
		}
		void Release()
		{
			if (--(*_pCount) == 0 && _ptr)
			{
				cout << "delete[]" << _ptr << endl;
				delete _ptr;
				delete _pCount;
				_pCount = nullptr;
				_ptr = nullptr;
			}
		}
		//sp1 = sp3
		shared_ptr<T>& operator=(shared_ptr<T>& sp)
		{
			 // 检测是否自身赋值
			//if (this != &sp)
			if (_ptr == sp._ptr) // 防止sp1 == sp2
			{
				Release();
				_ptr = sp._ptr;
				_pCount = sp._pCount;
				(*_pCount)++;
			}
			return *this;
		}

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

	private:
		T* _ptr;
		int* _pCount;
	};
}

std::shared_ptr的循环引用问题

struct ListNode
{
	ListNode* _next = nullptr;
	ListNode* _prev = nullptr;
	int _val = 0;
	~ListNode()
	{
		cout << "~ListNode()" << endl;
	}
};

int main()
{
	std::shared_ptr<ListNode> p1(new ListNode);
	std::shared_ptr<ListNode> p2(new ListNode);

	p1->_next = p2;
	p2->_prev = p1;

	return 0;
}

在这里插入图片描述
进行编译后报错,显示类型不匹配,这是因为在"p1->_next = p2;"中,p2是std::shared_ptr类型,而p1->_next 的类型我们从结构体中可以看出是ListNode*,所以编译就会报错.

那么我们如何解决如上问题呢?

因为我们要使用智能指针,所以将结构体中的类型进行更改为

struct ListNode
{
	std::shared_ptr<ListNode> _next = nullptr;
	std::shared_ptr<ListNode> _prev = nullptr;
	int _val = 0;
	~ListNode()
	{
		cout << "~ListNode()" << endl;
	}
};

编译后我们发现

在这里插入图片描述

p1,p2应该要进行销毁,去调用自己的析构函数,但是我们发现实际上却没有调用,所以一定造成了内存泄露,那么这是为什么呢?

引入循环引用问题:

在这里插入图片描述

在这里插入图片描述

画图分析如下:

在这里插入图片描述

在这里插入图片描述
接下来需要我们引入新的智能指针(std::weak_ptr)

在这里插入图片描述
这个weak_ptr可以接受shared_ptr进行传参,此时这个weak_ptr只负责连接,不负责去进行引用计数,这样就很好的解决了循环引用的问题!

struct ListNode
{
	std::weak_ptr<ListNode> _next;
	std::weak_ptr<ListNode> _prev;

	int _val = 0;
	~ListNode()
	{
		cout << "~ListNode()" << endl;
	}
};

int main()
{
	std::shared_ptr<ListNode> p1(new ListNode);
	std::shared_ptr<ListNode> p2(new ListNode);

	p1->_next = p2;
	p2->_prev = p1;

	return 0;
}

在这里插入图片描述

3.6 std::weak_ptr

	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)
		{
			if (_ptr != sp.Get())
			{
				_ptr = sp.Get();
			}
			return *this;
		}

	private:
		T* _ptr;
	};
	struct ListNode
{
	hulu::weak_ptr<ListNode> _next;
	hulu::weak_ptr<ListNode> _prev;

	int _val = 0;
	~ListNode()
	{
		cout << "~ListNode()" << endl;
	}
};

int main()
{
	hulu::shared_ptr<ListNode> p1(new ListNode);
	hulu::shared_ptr<ListNode> p2(new ListNode);


	p1->_next = p2;
	p2->_prev = p1;

	return 0;
}

在这里插入图片描述
weak_ptr不参与指向资源的释放管理!

四、定制删除器

如果不是new出来的对象如何通过智能指针管理呢?其实shared_ptr设计了一个删除器来解决这个问题!

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

// unique_ptr/shared_ptr 默认释放资源用的delete
int main()
{
	std::unique_ptr<Date> up1(new Date);
	std::unique_ptr<Date> up2(new Date[10]);
	std::unique_ptr<Date> up3((Date*)malloc(sizeof(Date)*10));

	return 0;
}

在这里插入图片描述

如何匹配申请方式去释放呢?

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

// unique_ptr/shared_ptr 默认释放资源用的delete

template<class T>
struct DeleteArray
{
	void operator()(T* ptr)
	{
		cout << "delete[] " <<ptr<< endl;
		delete[] ptr;
	}
};

template<class T>
struct Free
{
	void operator()(T* ptr)
	{
		cout << "free " << ptr << endl;
		free(ptr);
	}
};

int main()
{
	std::unique_ptr<Date> up1(new Date);
	std::unique_ptr<Date,DeleteArray<Date>> up2(new Date[10]);
	std::unique_ptr<Date,Free<Date>> up3((Date*)malloc(sizeof(Date)*10));

	return 0;
}

在这里插入图片描述

五、完整代码

#pragma once

namespace hulu
{
	template<class T>
	class SmartPtr
	{
	public:
		SmartPtr(T* ptr)
			:_ptr(ptr)
		{}
		~SmartPtr()
		{
			cout << "delete[]" << _ptr << endl;
			//delete[] _ptr;
			delete _ptr;
			_ptr = nullptr;
		}

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

	private:
		T* _ptr;
	};

	template<class T>
	class auto_ptr
	{
	public:
		auto_ptr() {}
		auto_ptr(T* ptr)
			:_ptr(ptr)
		{
			ptr = nullptr;
		}
		auto_ptr(auto_ptr<T>& sp)
			:_ptr(sp._ptr)
		{
			sp._ptr = nullptr;
		}

		auto_ptr<T>& operator=(auto_ptr<T>& sp)
		{
			//检测是不是自己给自己赋值
			if (this != &sp)
			{
				//释放当前对象的资源
				if (_ptr)
					delete _ptr;
				//转移sp中的资源到当前对象
				_ptr = sp._ptr;
				sp._ptr = nullptr;
			}
			return *this;
		}

		~auto_ptr()
		{
			if (_ptr)
			{
				cout << "delete[]" << _ptr << endl;
				delete _ptr;
				_ptr = nullptr;
			}

		}

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

	private:
		T* _ptr;
	};

	template<class T>
	struct default_delete
	{
		void operator()(T* ptr)
		{
			cout << "delete" << ptr << endl;
			delete ptr;
		}
	};


	template<class T,class D = default_delete<T>>
	class unique_ptr
	{
	public:
		unique_ptr(T* ptr)
			:_ptr(ptr)
		{}
		~unique_ptr()
		{
			if (_ptr)
			{
				/*cout << "delete[]" << _ptr << endl;
				delete _ptr;*/
				D del;
				del(_ptr);
				_ptr = nullptr;
			}
		}
		//像指针一样
		T& operator*()
		{
			return *_ptr;
		}
		T* operator->()
		{
			return _ptr;
		}

	private:
		unique_ptr(unique_ptr<T>& sp)= delete;
		unique_ptr<T>& operator=(const unique_ptr<T>& sp)= delete;
	private:
		T* _ptr;
	};

	template<class T, class D = default_delete<T>>
	class shared_ptr
	{
	public:
		shared_ptr(T* ptr)
			:_ptr(ptr),
			_pCount(new int(1))
		{}
		~shared_ptr()
		{
			Release();
		}
		shared_ptr(const shared_ptr<T>& sp)
			:_ptr(sp._ptr),
			_pCount(sp._pCount)
		{
			(*_pCount)++;
		}
		void Release()
		{
			if (--(*_pCount) == 0 && _ptr)
			{
				/*cout << "delete[]" << _ptr << endl;
				delete _ptr;*/
				D del;
				del(_ptr);
				delete _pCount;
				_pCount = nullptr;
				_ptr = nullptr;
			}
		}
		//sp1 = sp3
		shared_ptr<T>& operator=(shared_ptr<T>& sp)
		{
			 // 检测是否自身赋值
			//if (this != &sp)
			if (_ptr == sp._ptr) // 防止sp1 == sp2
			{
				Release();
				_ptr = sp._ptr;
				_pCount = sp._pCount;
				(*_pCount)++;
			}
			return *this;
		}

		//像指针一样
		T& operator*()
		{
			return *_ptr;
		}
		T* operator->()
		{
			return _ptr;
		}
		T* Get()const
		{
			return _ptr;
		}
	private:
		T* _ptr;
		int* _pCount;
	};

	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)
		{
			if (_ptr != sp.Get())
			{
				_ptr = sp.Get();
			}
			return *this;
		}

	private:
		T* _ptr;
	};
}

六、C+11和boost中智能指针的关系

  1. C++98中产生了第一个智能指针auto_ptr.
  2. C++boost给出了更实用的scoped_ptr和shared_ptr和weak_ptr.
  3. C++TR1,引入了shared_ptr等.不过注意的是TR1并不是标准版本.
  4. C++11中引入了unique_ptr,shared_ptr和weak_ptr.需要注意的是unique_ptr对应boost中的scoped_ptr.并且这些智能指针的实现原理是参考boost中实现的.

总结

(本章完)

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

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

相关文章

Beefxss使用教程图文教程(超详细)

「作者主页」&#xff1a;士别三日wyx 「作者简介」&#xff1a;CSDN top100、阿里云博客专家、华为云享专家、网络安全领域优质创作者 Beefxss一、首次使用二、修改账号密码三、自带练习页面四、简单使用五、工具界面介绍六、功能演示1、网页重定向2、社工弹窗3、功能颜色标识…

2022黑马Redis跟学笔记.实战篇(四)

2022黑马Redis跟学笔记.实战篇 四4.3.秒杀优惠券功能4.3.1.秒杀优惠券的基本实现一、优惠卷秒杀1.1 全局唯一ID1.2 Redis实现全局唯一Id1.3 添加优惠卷1.4 实现秒杀下单4.3.2.超卖问题4.3.3.基于乐观锁解决超卖问题1. 悲观锁2. 乐观锁3. 乐观锁解决超卖问题4.4 秒杀的一人一单限…

蓝桥杯stm32 USART 串口接收数据

文章代码使用 HAL 库。 文章目录 前言一、创建 CubeMX 工程:二、 中断接收数据 函数:三、串口接收回调函数实验效果四、接收固定长度的数据。五、串口接收 不定长数据。总结前言 上篇文章是 串口的发送数据,这篇文章接着上次的 讲 串口的接受数据。 一、创建 CubeMX 工程:…

信息安全(一)

思维导图 一、AES加解密 1.概述 1.1 概念 AES&#xff1a; 高级加密标准&#xff08;Advanced Encryption Standard&#xff09;是一种对称加密的区块加密标准。 &#xff08;1&#xff09;替代DES的新一代分组加密算法 &#xff08;2&#xff09;支持三种长度密钥&#x…

网络知识点梳理与总结

作者简介:一名云计算网络运维人员、每天分享网络与运维的技术与干货。 座右铭:低头赶路,敬事如仪 个人主页:网络豆的主页​​​​​​ 目录 前言 一.知识点梳理 前言 本章将会对高级网络应用一些知识点进行梳理。 一.知识点梳理 1.单臂的缺陷有哪些?

大学生博主-14天学习挑战赛活动-CSDN

还在为写文没有流量发愁吗&#xff1f;还沉浸在假期中无法恢复状态吗&#xff1f;赶快来参与面向CSDN的大学生博主而举办的活动吧&#xff01;本次活动为了避免刷量行为&#xff0c;也为了保持公平性&#xff0c;能够选出最优秀的文章&#xff0c;特意邀请了五位在C站具有一定影…

文华财经期货指标公式量化策略分析软件,多空共振信号准确率高的公式源码

期货指标公式信号本身就有滞后性&#xff0c;周期越大&#xff0c;滞后性越久。指标公式不是100%稳赚的工具&#xff0c;只是在合适的时候让我们理性看待行情&#xff0c;减少逆势操作。 多空量化三维系统是一款通过数学分析、挖掘价格运动规律&#xff0c;对历史价格走势、趋势…

SQLSERVER 临时表和表变量到底有什么区别?

一&#xff1a;背景 1. 讲故事 今天和大家聊一套面试中经常被问到的高频题&#xff0c;对&#xff0c;就是 临时表 和 表变量 这俩玩意&#xff0c;如果有朋友在面试中回答的不好&#xff0c;可以尝试看下这篇能不能帮你成功迈过。 二&#xff1a;到底有什么区别 1. 前置思…

了解僵尸网络攻击:什么是僵尸网络,它如何传播恶意软件以及如何保护自己?

进行系统安全安排的专业人员非常了解“僵尸网络”一词。通常用于被劫持的计算机/系统链&#xff0c;如果指示恢复性和健壮的系统&#xff0c;则应很好地理解“僵尸网络”一词&#xff0c;因为它们的错误使用会导致巨大的混乱。 文章目录前言一、僵尸网络定义僵尸网络如何工作&a…

HTTP API自动化测试从手工到平台的演变

不管是 Web 系统&#xff0c;还是移动 APP&#xff0c;前后端逻辑的分离设计已经是常态化&#xff0c;相互之间通过 API 调用进行数据交互。在基于 API 约定的开发模式下&#xff0c;如何加速请求 / 响应的 API 测试&#xff0c;让研发人员及早参与到调试中来呢&#xff1f;既然…

数据结构与算法-数组

前言&#xff1a;几乎所有的编程语言都原生支持数组类型。因为数组是最简单的内存数据结构。创建一个数组&#xff1a;let arr new Array()或let arr new Array(5) // 指定长度或let arr new Array(1,2,3,4,5) // 将数组元素作为参数传给构造函数或let arr [1,2,3,4,5] // …

字节6面,成功唬住面试官拿了27K,软件测试面试也没有传说中那么难吧....

字节的面试挺独特&#xff0c;每轮面试都没有 HR 约时间&#xff0c;一般是晚上 8 点左右面试官来一个电话&#xff0c;问是否能面试&#xff0c;能的话开始面&#xff0c;不能就约一个其它时间。全程 6 面&#xff0c;前五面技术面&#xff0c;电话面试&#xff0c;最后一面是…

Windows中的CMD不需要死记硬背

日期&#xff1a;2023年2月16日 作者&#xff1a;Commas 签名&#xff1a;(ง •_•)ง 积跬步以致千里,积小流以成江海…… 注释&#xff1a;如果您觉得有所帮助&#xff0c;帮忙点个赞&#xff0c;也可以关注我&#xff0c;我们一起成长&#xff1b;如果有不对的地方&#xf…

【React】setState修改状态

React(三) 修改状态 【数据驱动视图思想】 通过setState修改状态 作用&#xff1a; 修改state更新ui 语法&#xff1a;this.setState({要修改的部分数据}) &#x1f4a1; 注意&#xff1a;不能直接修改state中的数据&#xff0c;而是要设置新值去覆盖。 // 1. 导包 impor…

AFLNET lightftp项目报错解决方法

在学习AFLNET的时候&#xff0c;本人尝试对示例项目中的lightftp进行fuzz,而后出现如下报错&#xff1a; AFLNet - the states hashtable should always contain an entry of the initial state 在github项目issue里看到了有人的问题和我一摸一样&#xff0c;Stack Overflow里…

JavaScript随手笔记---轮播图(点击切换)

&#x1f48c; 所属专栏&#xff1a;【JavaScript随手笔记】 &#x1f600; 作  者&#xff1a;我是夜阑的狗&#x1f436; &#x1f680; 个人简介&#xff1a;一个正在努力学技术的CV工程师&#xff0c;专注基础和实战分享 &#xff0c;欢迎咨询&#xff01; &#…

【服务器数据恢复】Vsan逻辑架构出现故障的数据恢复案例

服务器数据恢复环境&#xff1a; VMWARE VSAN包含三台服务器节点&#xff1b; 每个服务器节点上配置2块SSD硬盘和4块机械硬盘&#xff1b; 每个服务器节点上创建两个磁盘组&#xff1b; 每个磁盘组采用1个SSD硬盘作为缓存盘&#xff0c;2个机械硬盘作为容量盘&#xff0c;三个服…

TypeScript快速入门———(二)TypeScript常用类型

文章目录概述1 类型注解2 常用基础类型概述3.原始类型4 数组类型5 类型别名6.函数类型7 对象类型8 接口9 元组10 类型推论11 类型断言12 字面量类型13 枚举14 any 类型15 typeof概述 TypeScript 是 JS 的超集&#xff0c;TS 提供了 JS 的所有功能&#xff0c;并且额外的增加了…

成为 Seatunnel 源码贡献者保姆级教程

Apache SeaTunnel 是下一代高性能、分布式、海量数据集成平台&#xff0c;已经在 B 站、腾讯云等 100 家公司生产使用。目前处于 incubator 阶段。作为公司内部使用的 ETL 工具&#xff0c;Seatunnel 可以基于已有的 Spark、Flink 计算平台进行数据交换也可以运行在 k8s 平台上…

【ESP32+freeRTOS学习笔记-(八)资源管理】

目录1、 资源使用概况2、互斥方法之一&#xff1a;基本临界区2.1、taskENTER_CRITICAL_FROM_ISR() 和taskEXIT_CRITICAL_FROM_ISR()3、互斥方法之二&#xff1a;挂起或锁定调度程序3.1 vTaskSuspendAll()3.2 xTaskResumeAll()4 互斥方法三&#xff1a;互斥信号量&#xff08;和…