C++智能指针[下](shared_ptr/weak_ptr/循环引用/删除器)

文章目录

  • 4.智能指针[shared_ptr]
    • 4.1设计理念
      • 成员属性
    • 4.2主要接口
      • 拷贝构造
    • 4.3引用计数线程安全问题
      • 测试线程安全
        • 通过对计数引用的加锁保护使得类线程安全
        • 类实例化的对象使用时需要手动加锁保护
      • "锁"的引进
      • 线程引用传参问题
    • 4.4整体代码
  • 5.循环引用问题
    • 5.1问题的引入
    • 5.2分析造成此问题的原因
    • 5.3weak_ptr的主要代码
  • 6.数组对象的删除问题
    • 6.1代码问题
    • 6.2std::shared_ptr面对此问题的解决方案
      • 1.首先看std::shared_ptr::~shared_ptr
      • 2.删除器的传参及使用
      • 3.添加封装删除器
  • 7.总结
    • 7.1完整代码
    • 7.2C++11和Boost智能指针的关系

4.智能指针[shared_ptr]

4.1设计理念

成员属性

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

每一个对象除了有一个主指针外 还有一个副指针用来计数 为什么不设置成int而设置成int*?

同一块空间被两个指针指向 当编译器得到需要销毁指针的指令时 会先判断这是不是最后一个指针 即count==1 时才释放空间 其余情况均只是的计数-- 而不释放空间 这是我们的最初目的 如果设置成int意味着每一个对象有各自的count 当ptr1拷贝给ptr2 我们想要的是 有一块空间单独来计数 如果执行拷贝则计数++ 即我们需要一块共有的空间来实现

不是要实现共有吗 为什么不直接用静态变量?

静态变量可以实现共有没错 但他是当前类的所有对象共有 举例说明: 代码的本意是ptr1 ptr2 ptr3指向同一块空间 此时count == 3 ptr4指向另一块空间时 代码的本意是count = 1 但是同时改变了ptr1/2/3

4.2主要接口

拷贝构造

在这里插入图片描述

	//赋值重载
	//1."自己"给"自己"赋值
	//不仅仅是p1 = p1 还要考虑p2 = p1 但之前p2就 = p1
	//2.左 = 右 赋值后 
	// 左指针指向空间的指针少了一个 左指针的count-- 进一步考虑count--后==0的情况
	// 右指针指向空间的指针多了一个 右指针的count++
	shared_ptr<T>& operator=(const shared_ptr<T>& sp)
	{
		//"自己"给"自己"赋值: "自己"的本质
		if (_ptr != sp._ptr)
		{
			//count--
			Release();

			_ptr = sp._ptr;
			_pcount = sp._pcount;
			_pmtx = sp._pmtx;

			//count++
			AddCount();
		}

		return *this;
	}

4.3引用计数线程安全问题

测试线程安全

通过对计数引用的加锁保护使得类线程安全
	struct Date
	{
		int _year = 0;
		int _month = 0;
		int _day = 0;
	};
	void SharePtrFunc(ape::shared_ptr<Date>& sp, size_t n, mutex& mtx)
	{
		cout << "   SharePtrFunc: sp.GetPtr() == " << sp.GetPtr() << endl;

		for (size_t i = 0; i < n; ++i)
		{
			ape::shared_ptr<Date> copy(sp);
		}
	}

	void test_shared_safe()
	{
		ape::shared_ptr<Date> p(new Date);
		cout << "test_shared_safe: p.GetPtr() == " << p.GetPtr() << endl;

		const size_t n = 10000;
		mutex mtx;

		//线程引用传参即便mtx已经是引用 此处仍然需要使用库函数ref();
		thread t1(SharePtrFunc, ref(p), n, ref(mtx));
		thread t2(SharePtrFunc, ref(p), n, ref(mtx));

		t1.join();
		t2.join();

		cout << "p.GetCount(): == " << p.GetCount() << endl;
	}

在这里插入图片描述

类实例化的对象使用时需要手动加锁保护

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

"锁"的引进

在这里插入图片描述

线程引用传参问题

在这里插入图片描述

简单理解为 p在传给sp前是需要先调用线程的构造函数的 期间发生了某种动作 使得失去引用属性

4.4整体代码

template<class T>
class shared_ptr
{
public:
	//构造函数
	shared_ptr(T* ptr)
		:_ptr(ptr)
		, _pcount(new int(1))
		, _pmtx(new mutex)
	{

	}

	void Release()
	{
		//上锁
		_pmtx->lock();
		//不可释放锁
		bool deleteFlag = false;

		if (--(*_pcount) == 0)
		{
			cout << "delete:" << _ptr << endl;
			delete _ptr;
			delete _pcount;
			//可释放锁
			deleteFlag = true;
		}
		//解锁
		_pmtx->unlock();
		//判断并释放锁
		if (deleteFlag)
		{
			delete _pmtx;
		}
	}
	//void Release()
	//{
	//	_pmtx->lock();
	//	bool deleteFlag = false;
	//	if (--(*_pcount) == 0)
	//	{
	//		if (_ptr)
	//		{
	//			//cout << "delete:" << _ptr << endl;
	//			//delete _ptr;
	//
	//			// 删除器进行删除
	//			_del(_ptr);
	//		}
	//
	//		delete _pcount;
	//		deleteFlag = true;
	//	}
	//
	//	_pmtx->unlock();
	//
	//	if (deleteFlag)
	//	{
	//		delete _pmtx;
	//	}
	//}
	void AddCount()
	{
		_pmtx->lock();

		++(*_pcount);

		_pmtx->unlock();
	}

	//拷贝构造
	shared_ptr(const shared_ptr<T>& sp)
		:_ptr(sp._ptr)
		, _pcount(sp._pcount)
		, _pmtx(sp._pmtx)
	{
		AddCount();
	}

	//赋值重载
	//1."自己"给"自己"赋值
	//不仅仅是p1 = p1 还要考虑p2 = p1 但之前p2就 = p1
	//2.左 = 右 赋值后 
	// 左指针指向空间的指针少了一个 左指针的count-- 进一步考虑count--后==0的情况
	// 右指针指向空间的指针多了一个 右指针的count++
	shared_ptr<T>& operator=(const shared_ptr<T>& sp)
	{
		//"自己"给"自己"赋值: "自己"的本质
		if (_ptr != sp._ptr)
		{
			//count--
			Release();

			_ptr = sp._ptr;
			_pcount = sp._pcount;
			_pmtx = sp._pmtx;

			//count++
			AddCount();
		}

		return *this;
	}

	//析构函数
	~shared_ptr()
	{
		Release();
	}

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

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

	T* GetPtr()
	{
		return _ptr;
	}

	int GetCount()
	{
		return *_pcount;
	}

private:
	T* _ptr;
	int* _pcount;
	mutex* _pmtx;
};

void test_shared()
{
	shared_ptr<int> sp1(new int(1));
	shared_ptr<int> sp2(sp1);
	shared_ptr<int> sp3(sp2);

	shared_ptr<int> sp4(new int(10));

	sp1 = sp4;
	sp4 = sp1;

	sp1 = sp1;
	sp1 = sp2;
}


//   线程安全问题   ///
struct Date
{
	int _year = 0;
	int _month = 0;
	int _day = 0;
};

void SharePtrFunc(ape::shared_ptr<Date>& sp, size_t n, mutex& mtx)
{
	cout << "   SharePtrFunc: sp.GetPtr() == " << sp.GetPtr() << endl;

	cout << "   SharePtrFunc: &sp == " << &sp << endl;

	for (size_t i = 0; i < n; ++i)
	{
		ape::shared_ptr<Date> copy(sp);

		mtx.lock();

		sp->_year++;
		sp->_day++;
		sp->_month++;

	    mtx.unlock();
	}
}

void test_shared_safe()
{
	ape::shared_ptr<Date> p(new Date);
	cout << "test_shared_safe: p.GetPtr() == " << p.GetPtr() << endl;

	cout << "test_shared_safe: &p == " << &p << endl;

	const size_t n = 100000;
	mutex mtx;

	//线程引用传参即便p和mtx已经是引用 此处仍然需要使用库函数ref();
	thread t1(SharePtrFunc, ref(p), n, ref(mtx));
	thread t2(SharePtrFunc, ref(p), n, ref(mtx));

	t1.join();
	t2.join();

	cout << "p.GetCount(): == " << p.GetCount() << endl;

	cout << "p->_year  == " << p->_year << endl;
	cout << "p->_month == " << p->_month << endl;
	cout << "p->_month == " << p->_month << endl;

}

5.循环引用问题

5.1问题的引入

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

在这里插入图片描述

5.2分析造成此问题的原因

在这里插入图片描述

为了解决此问题 需要引进weaked_ptr 我们需要了解的是
智能指针shared_ptr满足

  1. 符合RAII思想
  2. 可以像指针一样使用
  3. 支持拷贝

智能指针weaked_ptr满足

  1. 不符合RAII思想
  2. 可以像指针一样使用
  3. 辅助解决shared_ptr的循环引用问题
  4. weaked_ptr可以指向资源,但是不参与管理,不增加引用计数

实际上库里的智能指针远比我们上述讲到的复杂得多 为了便于学习和理解 我们只学习核心框架 需要了解的是 库里所支持的这两个函数在这里插入图片描述
weaked_ptr有自己的count 配合expired来判断所指向资源是否还有被指向的必要 即weaked_ptr可以指向资源,但是不参与管理,不增加shared_ptr引用计数 所以存在于一种所指向资源的计数已经成0 此时expired判断是否失效 若所指向资源失效weaked_ptr就不再指向

在这里插入图片描述

5.3weak_ptr的主要代码

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

	weak_ptr(const shared_ptr<T>& sp)
		:_ptr(sp.GetPtr())
	{
	
	}

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

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

	T* GetPtr()
	{
		return _ptr;
	}

private:
	T* _ptr;
};

6.数组对象的删除问题

6.1代码问题

ape::shared_ptr<Date> sparr(new Date[10]);
当使用我们自己模拟实现的简洁版shared_ptr时 上述代码会报错(手动实现Date的析构函数时会报错 不手动实现调用库的析构函数不报错 因为当手动实现了析构函数 Date的空间前会有一个4字节的空间用来存放实例化对象的个数 以便知道调用几次析构函数 但是此时析构函数应该先向前偏移4字节 以便析构时把这4个字节也释放

6.2std::shared_ptr面对此问题的解决方案

1.首先看std::shared_ptr::~shared_ptr

在这里插入图片描述

即库里的shared_ptr::~shared_ptr与我们写的析构函数不同之处在于 一个对象在实例化时 他会判断是否接受了参数deleter 如果接收则析构时调用deleter析构 若没有接收deleter则正常析构

2.删除器的传参及使用

在这里插入图片描述

销毁对象。但是,以前,根据成员use_count的值,它可能会产生以下副作用:如果use_count大于1(即该对象与其他shared_ptr对象共享其托管对象的所有权):与其共享所有权的其他对象的使用计数将减少1。如果use_coount为1(即,该对象是托管指针的唯一所有者):它所拥有的指针被删除(如果shared_ptr对象是用特殊的deleter构造的,则调用它;否则,函数使用运算符delete)。如果use_count为零(即对象为空),则此析构函数没有副作用。

	template<class T>
	struct DeleteArray
	{
		void operator()(T* ptr)
		{
			cout << "匿名对象DeleteArray<Date>(): " << ptr << endl;
			delete[] ptr;
		}
	};
	void test_std_shared_deletor()
	{
		//template <class U, class D> 
		//shared_ptr (U* p, D del);   带删除器的构造函数

		std::shared_ptr<Date> sparr1(new Date[10], DeleteArray<Date>());

		std::shared_ptr<Date> sparr2(new Date[10],
			[](Date* ptr)
			{
				cout << "lambda表达式 delete[]: " << ptr << endl;
				delete[] ptr;
			}
		);

		auto deleter = [](Date* ptr)
			{
				cout << "lambda表达式 delete[]: " << ptr << endl;
				delete[] ptr;
			};
		std::shared_ptr<Date> sparr3(new Date[10], deleter);

		std::shared_ptr<FILE> spFile(fopen("Test.cpp", "r"),
			[](FILE* ptr)
			{
				cout << "lambda表达式 delete[]: " << ptr << endl;
				fclose(ptr);
			}
		);
	}

在这里插入图片描述

3.添加封装删除器

在这里插入图片描述

在这里插入图片描述

7.总结

7.1完整代码

#pragma once

#include <mutex>
#include <thread>
#include <memory>

namespace ape
{
	template<class T>
	class shared_ptr
	{
	public:
		//构造函数
		shared_ptr(T* ptr = nullptr)
			:_ptr(ptr)
			, _pcount(new int(1))
			, _pmtx(new mutex)
		{

		}

		//删除器构造函数
		template<class D>
		shared_ptr(T* ptr, D del)
			:_ptr(ptr)
			, _pcount(new int(1))
			, _pmtx(new mutex)
			, _del(del)
		{
		
		}

		//非删除器Release()
		/*
		void Release()
		{
			//上锁
			_pmtx->lock();
			//不可释放锁
			bool deleteFlag = false;

			if (--(*_pcount) == 0)
			{
				if (_ptr != nullptr)
				{
					cout << "delete:" << _ptr << endl;
					delete _ptr;
				}
				
				delete _pcount;
				//可释放锁
				deleteFlag = true;
			}
			//解锁
			_pmtx->unlock();
			//判断并释放锁
			if (deleteFlag)
			{
				delete _pmtx;
			}
		}
		*/
		
		//删除器Release()
		void Release()
		{
			_pmtx->lock();
			bool deleteFlag = false;

			if (--(*_pcount) == 0)
			{
				if (_ptr != nullptr)
				{
					_del(_ptr);
				}
		
				delete _pcount;
				deleteFlag = true;
			}
		
			_pmtx->unlock();
		
			if (deleteFlag)
			{
				delete _pmtx;
			}
		}
		void AddCount()
		{
			_pmtx->lock();

			++(*_pcount);

			_pmtx->unlock();
		}

		//拷贝构造
		shared_ptr(const shared_ptr<T>& sp)
			:_ptr(sp._ptr)
			, _pcount(sp._pcount)
			, _pmtx(sp._pmtx)
		{
			AddCount();
		}

		//赋值重载
		//1."自己"给"自己"赋值
		//不仅仅是p1 = p1 还要考虑p2 = p1 但之前p2就 = p1
		//2.左 = 右 赋值后 
		// 左指针指向空间的指针少了一个 左指针的count-- 进一步考虑count--后==0的情况
		// 右指针指向空间的指针多了一个 右指针的count++
		shared_ptr<T>& operator=(const shared_ptr<T>& sp)
		{
			//"自己"给"自己"赋值: "自己"的本质
			if (_ptr != sp._ptr)
			{
				//count--
				Release();

				_ptr = sp._ptr;
				_pcount = sp._pcount;
				_pmtx = sp._pmtx;

				//count++
				AddCount();
			}

			return *this;
		}

		//析构函数
		~shared_ptr()
		{
			Release();
		}

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

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

		T* GetPtr() const
		{
			return _ptr;
		}

		int GetCount() const
		{
			return *_pcount;
		}

	private:
		T* _ptr;
		int* _pcount;
		mutex* _pmtx;

		//包装器
		//缺省值处理情况: ape::shared_ptr<Date> sp(new Date);
		//因为你析构时默认使用删除器 那么遇到没有显示传的要使用缺省值

		//构造函数传deleter时 可以是仿函数 lambda表达式 函数指针 此处用包装器接收
		function<void(T*)> _del = [](T* ptr)
			{
				cout << "lambda表达式 delete: " << ptr << endl;
				delete ptr;
			};
	};

	void test_shared()
	{
		shared_ptr<int> sp1(new int(1));
		shared_ptr<int> sp2(sp1);
		shared_ptr<int> sp3(sp2);

		shared_ptr<int> sp4(new int(10));

		sp1 = sp4;
		sp4 = sp1;

		sp1 = sp1;
		sp1 = sp2;
	}


	//   线程安全问题   ///
	struct Date
	{
		int _year = 0;
		int _month = 0;
		int _day = 0;

		~Date()
		{

		}
	};

	void SharePtrFunc(ape::shared_ptr<Date>& sp, size_t n, mutex& mtx)
	{
		cout << "   SharePtrFunc: sp.GetPtr() == " << sp.GetPtr() << endl;

		cout << "   SharePtrFunc: &sp == " << &sp << endl;

		for (size_t i = 0; i < n; ++i)
		{
			ape::shared_ptr<Date> copy(sp);

			mtx.lock();

			sp->_year++;
			sp->_day++;
			sp->_month++;

		    mtx.unlock();
		}
	}

	void test_shared_safe()
	{
		ape::shared_ptr<Date> p(new Date);
		cout << "test_shared_safe: p.GetPtr() == " << p.GetPtr() << endl;

		cout << "test_shared_safe: &p == " << &p << endl;

		const size_t n = 100000;
		mutex mtx;

		//线程引用传参即便p和mtx已经是引用 此处仍然需要使用库函数ref();
		thread t1(SharePtrFunc, ref(p), n, ref(mtx));
		thread t2(SharePtrFunc, ref(p), n, ref(mtx));

		t1.join();
		t2.join();

		cout << "p.GetCount(): == " << p.GetCount() << endl;

		cout << "p->_year  == " << p->_year << endl;
		cout << "p->_month == " << p->_month << endl;
		cout << "p->_month == " << p->_month << endl;

	}

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

		weak_ptr(const shared_ptr<T>& sp)
			:_ptr(sp.GetPtr())
		{
		
		}

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

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

		T* GetPtr()
		{
			return _ptr;
		}

	private:
		T* _ptr;
	};

	// 循环引用
	struct ListNode
	{

		/*普通写法
		ListNode* _next;
		ListNode* _prev;
		int _val;
		*/

		
		/*shared_ptr
		ape::shared_ptr<ListNode> _next;
		ape::shared_ptr<ListNode> _prev;
		int _val;
		*/

		//weaked_ptr
		ape::weak_ptr<ListNode> _next;
		ape::weak_ptr<ListNode> _prev;
		int _val;

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

	// 循环引用
	void test_shared_cycle()
	{
		/*
		常规写法 -- 抛异常场景不适合
		ListNode* n1 = new ListNode;
		ListNode* n2 = new ListNode;

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

		delete n1;
		delete n2;
		*/

		//智能指针写法
		ape::shared_ptr<ListNode> n1(new ListNode);
		ape::shared_ptr<ListNode> n2(new ListNode);

		//内置类型 = 自定义类型 -- error
		/*
		ListNode* _next;
		ListNode* _prev;
		int _val;

		ape::shared_ptr<ListNode> n1(new ListNode);
		ape::shared_ptr<ListNode> n2(new ListNode);

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

		/*shared_ptr: 引发循环引用问题
		ape::shared_ptr<ListNode> _next;
		ape::shared_ptr<ListNode> _prev;
		int _val;

		ape::shared_ptr<ListNode> n1(new ListNode);
		ape::shared_ptr<ListNode> n2(new ListNode);

		n1->_next = n2;
		n2->_prev = n1;
		*/
		
		/*weak_ptr: 解决循环引用
		ape::weak_ptr<ListNode> _next;
		ape::weak_ptr<ListNode> _prev;
		int _val;

		ape::shared_ptr<ListNode> n1(new ListNode);
		ape::shared_ptr<ListNode> n2(new ListNode);
        */
		cout << "n1.GetCount() == " << n1.GetCount() << endl;
		cout << "n2.GetCount() == " << n2.GetCount() << endl;
		n1->_next = n2;
		n2->_prev = n1;
		cout << "n1.GetCount() == " << n1.GetCount() << endl;
		cout << "n2.GetCount() == " << n2.GetCount() << endl;

	}

/
	 //定制删除器 -- 可调用对象
	
	template<class T>
	struct DeleteArray
	{
		void operator()(T* ptr)
		{
			cout << "匿名对象DeleteArray<Date>(): " << ptr << endl;
			delete[] ptr;
		}
	};

	//std库deleter的学习
	/*
	void test_std_shared_deletor()
	{
		//template <class U, class D> 
		//shared_ptr (U* p, D del);   带删除器的构造函数

		std::shared_ptr<Date> sparr1(new Date[10], DeleteArray<Date>());

		std::shared_ptr<Date> sparr2(new Date[10],
			[](Date* ptr)
			{
				cout << "lambda表达式 delete[]: " << ptr << endl;
				delete[] ptr;
			}
		);

		auto deleter = [](Date* ptr)
			{
				cout << "lambda表达式 delete[]: " << ptr << endl;
				delete[] ptr;
			};
		std::shared_ptr<Date> sparr3(new Date[10], deleter);

		std::shared_ptr<FILE> spFile(fopen("Test.cpp", "r"),
			[](FILE* ptr)
			{
				cout << "lambda表达式 delete[]: " << ptr << endl;
				fclose(ptr);
			}
		);
	}
	*/

	//删除器构造函数
	/*
	template<class D>
	shared_ptr(T* ptr, D del)
		:_ptr(ptr)
		, _pcount(new int(1))
		, _pmtx(new mutex)
		, _del(del)
	{

	}
	*/
	void test_ape_shared_deleter()
	{ 
		ape::shared_ptr<Date> sp(new Date);

		ape::shared_ptr<Date> sparr1(new Date[10], DeleteArray<Date>());
		ape::shared_ptr<Date> sparr2(new Date[10], 
			[](Date* ptr) 
			{
			cout << "lambda表达式 delete[]: " << ptr << endl;
			delete[] ptr;
			}
		);

		auto deleter = [](Date* ptr)
			{
				cout << "lambda表达式 delete[]: " << ptr << endl;
				delete[] ptr;
			};
		ape::shared_ptr<Date> sparr3(new Date[10], deleter);

		ape::shared_ptr<FILE> spFile(fopen("Test.cpp", "r"),
			[](FILE* ptr)
			{
			cout << "lambda表达式 delete[]: " << ptr << endl;
			fclose(ptr);
			}
		);
	}
}


7.2C++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实现]

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

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

相关文章

Java多线程秘籍,掌握这5种方法,让你的代码优化升级

介绍5种多线程方法&#xff0c;助您提高编码效率&#xff01; 如果您的应用程序与那些能够同时处理多个任务的应用程序相比表现不佳&#xff0c;很可能是因为它是单线程的。解决这个问题的方法之一是采用多线程技术。 以下是一些可以考虑的方法&#xff1a; 线程&#xff08;…

超声波测距与倒车雷达电路1

文章目录 超声测距 超声测距 超声测距跟倒车雷达绝大多数用的都是40kHz 接受是一个同相比例整流后加上一个比较器 换能器自带滤波&#xff0c;需要激发信号与换能器信号匹配 这个电路图是错的&#xff0c;一直不停的发&#xff0c;底下来不及收 频率越高传输距离…

解决使用WebTestClient访问接口报[185c31bb] 500 Server Error for HTTP GET “/**“

解决使用WebTestClient访问接口报[185c31bb] 500 Server Error for HTTP GET "/**" 问题发现问题解决 问题发现 WebTestClient 是 Spring WebFlux 框架中提供的用于测试 Web 请求的客户端工具。它可以不用启动服务器&#xff0c;模拟发送 HTTP 请求并验证服务器的响…

电脑怎么共享屏幕?电脑屏幕共享软件分享!

如何控制某人的电脑屏幕&#xff1f; 有时我们可能需要远程控制某人的计算机屏幕&#xff0c;例如&#xff0c;为我们的客户提供远程支持&#xff0c;远程帮助朋友或家人解决计算机问题&#xff0c;或在家中与同事完成团队合作。那么&#xff0c;电脑怎么共享屏幕&#xff…

SD-WAN让跨境网络访问更快、更安全!

目前许多外贸企业都面临着跨境网络不稳定、不安全的问题&#xff0c;给业务合作带来了很多困扰。但是&#xff0c;现在有一个解决方案能够帮助您解决这些问题&#xff0c;让您的跨境网络访问更快、更安全&#xff0c;那就是SD-WAN&#xff01; 首先&#xff0c;让我们来看看SD-…

时序预测 | Python实现ARIMA-LSTM自回归移动差分模型结合长短期记忆神经网络时间序列预测

时序预测 | Python实现ARIMA-LSTM自回归移动差分模型结合长短期记忆神经网络时间序列预测 目录 时序预测 | Python实现ARIMA-LSTM自回归移动差分模型结合长短期记忆神经网络时间序列预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 时序预测 | Python实现ARIMA-LSTM自…

PostgreSQL12中浮点数输出算法优化带来的小问题

最近碰到同事发来这样两个SQL&#xff0c;开发反馈输出的结果异常。 bill# select 0.1284*100::float;?column? --------------------12.839999999999998 (1 row)bill# select (0.1284*100)::float;float8 --------12.84 (1 row) 乍一看其实能看出明显的区别&#xff0c;由于…

vscode推送gitee方法

有一套uni-app代码需要修改&#xff0c;版本控制使用vscode的git功能&#xff0c;远程库在gitee上。 1、设置vscode中git.exe路径 由于git使用了绿色便携版&#xff08;PortableGit-2.42.0.2-64-bit.7z.exe&#xff09;&#xff0c;vscode未识别到git安装路径&#xff0c;需要…

Zynq UltraScale+ XCZU15EG 纯VHDL解码 IMX214 MIPI 视频,2路视频拼接输出,提供vivado工程源码和技术支持

目录 1、前言免责声明 2、我这里已有的 MIPI 编解码方案3、本 MIPI CSI2 模块性能及其优越性4、详细设计方案设计原理框图IMX214 摄像头及其配置D-PHY 模块CSI-2-RX 模块Bayer转RGB模块伽马矫正模块VDMA图像缓存Video Scaler 图像缓存DP 输出 5、vivado工程详解PL端FPGA硬件设计…

一、高效构建Java应用:Maven入门和进阶

一、高效构建Java应用&#xff1a;Maven入门和进阶 目录 一、Maven简介和快速入门 1.1 Maven介绍1.2 Maven主要作用理解1.3 Maven安装和配置 二、基于IDEA的Maven工程创建 2.1梳理Maven工程GAVP属性2.2 Idea构建Maven JavaSE工程2.3 Idea构建Maven JavaEE工程2.4 Maven工程项…

GaussDB数据库管理系统介绍

1.GaussDB的发展 2.GaussDB的生态 内部&#xff1a; 云化自动化方案。通过数据库运行基础设施的云化将DBA(数据库管理员)和运维人员的日常工作 自动化。外部&#xff1a; 采用与数据库周边生态伙伴对接与认证的生态连接融合方案&#xff0c;解决开发者/DBA难获取、应用难对接等…

【详细】Java网络通信 TCP、UDP、InetAddress

一、网络程序设计基础 1.局域网与因特网 为了实现两台计算机的通信&#xff0c;必须用一个网络线路连接两台计算机&#xff08;服务器<-->网络<-->客户机&#xff09;。 服务器是指提供信息的计算机或程序&#xff0c;客户机是指请求信息的计算机或程序。网络用…

基于ResNet34的花朵分类

一.数据集准备 新建一个项目文件夹ResNet&#xff0c;并在里面建立data_set文件夹用来保存数据集&#xff0c;在data_set文件夹下创建新文件夹"flower_data"&#xff0c;点击链接下载花分类数据集https://storage.googleapis.com/download.tensorflow.org/example_i…

北京筑龙发声炼化企业大会,助力央国企采购供应链数字化转型

10月25日&#xff0c;以“科技创新引领高质量发展&#xff0c;夯实炼化自立自强根基”为主题的第四届炼化企业创新发展大会暨新技术与解决方案交流会”在浙江省宁波市盛大召开。北京筑龙智能化业务部高级咨询顾问王良受邀出席&#xff0c;带来主题为“智能物料——企业采购供应…

【Python】基于非侵入式负荷检测与分解的电力数据挖掘

文章目录 前言一、案例背景二、分析目标三、分析过程四、数据准备4.1 数据探索4.2 缺失值处理 五、属性构造5.1 设备数据5.2 周波数据 六、模型训练七、性能度量文末送书&#xff1a;《Python数据挖掘&#xff1a;入门、进阶与实用案例分析》 前言 本案例将根据已收集到的电力…

windows模拟触摸

安装EcoTUIODriver驱动 . 安装完后电脑属性显示笔和触控为为20点触摸点提供笔和触控支持。 在另一台电脑上运行tuio模块器是一个jar文件,TuioSimulator默认是给本机的3333端口发送TUIO消息&#xff0c;可通过-host指定远端主机&#xff0c;-port指定端口号 执行命令: java …

jvm垃圾回收算法有哪些及原理

目录 垃圾回收器1 Serial收集器2 Parallel收集器3 ParNew收集器4 CMS收集器5 G1回收器三色标记算法标记算法的过程三色标记算法缺陷多标漏标 垃圾回收器 垃圾回收机制&#xff0c;我们已经知道什么样的对象会成为垃圾。对象回收经历了什么——垃圾回收算法。那么谁来负责回收垃…

NPDP产品经理证书是什么行业的证书?

NPDP是一个跨行业的证书&#xff0c;它适用于各种不同类型和规模的组织。无论是制造业、服务业还是科技领域&#xff0c;都可以从NPDP认证中获益。 1. 制造业&#xff1a; 制造业涉及大量的产品开发和创新活动。从汽车制造到电子设备制造&#xff0c;从家居用品到航天航空&…

当数据库遇上深度学习:AI DataLoader 助力因子管理模型训练全流程

深度学习模型有能力自动发现变量之间的关系&#xff0c;而这些关系通常是不可见的&#xff0c;这使得深度学习可以挖掘新的因子和规律&#xff0c;为量化投资策略提供更多可能性。在传统的量化策略开发流程中&#xff0c;通常会使用 Python 或第三方工具生成因子&#xff0c;并…

JVM | 命令行诊断与调优 jhsdb jmap jstat jps

目录 jmap 查看堆使用情况 查看类列表&#xff0c;包含实例数、占用内存大小 生成jvm的堆转储快照dump文件 jstat 查看gc的信息&#xff0c;查看gc的次数&#xff0c;及时间 查看VM内存中三代&#xff08;young/old/perm&#xff09;对象的使用和占用大小 查看元数据空…