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

news2024/12/26 22:51:03

文章目录

  • 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实现]

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

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

相关文章

算法通关村第二关-黄金挑战K个一组反转

大家好我是苏麟 , 今天带来K个一组反转 , K个一组反转 可以说是链表中最难的一个问题了&#xff0c;每k 个节点一组进行翻转&#xff0c;请你返回翻转后的链表。k 是一个正整数&#xff0c;它的值小于或等于链表的长度。如果节点总数不是 k 的整数倍&#xff0c;那么请将最后…

为“数字盲道”修“护栏”,隐语YACL护航无障碍数字服务隐私安全

“隐语”是开源的可信隐私计算框架&#xff0c;内置 MPC、TEE、同态等多种密态计算虚拟设备供灵活选择&#xff0c;提供丰富的联邦学习算法和差分隐私机制 开源项目&#xff1a;github.com/secretflowgitee.com/secretflow 10月19日&#xff0c;杭州第4届亚洲残疾人运动会火炬…

cola架构:有限状态机(FSM)源码分析

目录 0. cola状态机简述 1.cola状态机使用实例 2.cola状态机源码解析 2.1 语义模型源码 2.1.1 Condition和Action接口 2.1.2 State 2.1.3 Transition接口 2.1.4 StateMachine接口 2.2 Builder模式 2.2.1 StateMachine Builder模式 2.2.2 ExternalTransitionBuilder-…

重生奇迹MU上武器加13有加成吗?

重生奇迹一身加13的额外属性在哪看 装备页面。在角色扮演类游戏《重生奇迹mu》中&#xff0c;玩家可以在装备页面查看一身加13的额外属性&#xff0c;该道具能为玩家提供攻击力加成&#xff0c;输出更多的伤害。 重生奇迹13绝望之仗需要多少力量 16力量。重生奇迹mu加13绝望…

掌动智能APP自动化测试工具的主要功能

掌动智能APP自动化测试工具是一类用于自动执行测试脚本和验证移动应用程序的工具。它们模拟用户与应用程序的交互&#xff0c;以便检查应用程序的功能、性能和稳定性。这些工具可以用于各种移动平台&#xff0c;包括iOS和Android。本文将介绍APP自动化测试工具的主要功能有哪些…

大数据-Storm流式框架(四)---storm容错机制

1、集群节点宕机 Nimbus服务器 硬件 单点故障&#xff1f;可以搭建HA jStorm搭建 nimbus的HA nimbus的信息存储到zookeeper中&#xff0c;只要下游没问题&#xff08;进程退出&#xff09;nimbus退出就不会有问题&#xff0c; 如果在nimbus宕机&#xff0c;也不能提交…

DC-9 靶机

DC_9 信息搜集 存活检测 详细扫描 后台网页扫描 漏洞利用 漏洞发现 在 search 页面的输入框尝试 sql 注入 1 or 11#返回大量数据 sqlmap 爆破数据库 sqlmap -u http://10.4.7.154/search.php --dbs --batch 爆破失败&#xff0c;显示未找到参数 抓包查看 爆破数据库 …

iPhone手机屏幕分辨率

ios app测试时&#xff0c;需要测试应用在不同型号的苹果手机上的表现形式&#xff0c;可以自己在浏览器上配置。 代数设备逻辑像素尺寸缩放发布时间第一代iPhone 2G320 x 480480 x 3203.5寸1x2007年6月29日第二代iPhone 3320 x 480480 x 3203.5寸1x2008年7月11日第三代iPhone …

ThinkPHP8学习笔记

ThinkPHP8官方文档地址&#xff1a;ThinkPHP官方手册 一、composer换源 1、查看 composer 配置的命令composer config -g -l 2、禁用默认源镜像命令composer config -g secure-http false 3、修改为阿里云镜像源composer config -g repo.packagist composer https://mirror…

JoySSL-新兴国产品牌数字证书

随着我国对数据安全重视程度的不断提升&#xff0c;国产SSL证书越来越受到广大政府机关和企业的青睐&#xff0c;成为提升网站数据安全能力的重要技术手段。那么什么是国产SSL证书&#xff1f;国产SSL证书和普通SSL证书又有什么区别呢&#xff1f; 什么是国产SSL证书&#xff…

封神工具:腾讯云服务器价格计算器_好工具分享

腾讯云价格计算器&#xff1a;可以计算腾讯云服务器不同CVM实例规格、CPU内存、公网带宽和系统盘费用明细表&#xff0c;可以一键计算出精准报价明细表&#xff0c;腾讯云服务器网txyfwq.com分享大家腾讯云服务器价格计算器入口链接、使用方法及限制说明&#xff1a; 腾讯云服…

优思学院:六西格玛培训中的“绿带”和“黑带”

在当今竞争激烈的商业世界中&#xff0c;提高效率、减少浪费是每个组织都追求的目标。为了达到这一目标&#xff0c;六西格玛管理方法为组织提供了一个卓越的工具。在六西格玛培训中&#xff0c;我们经常会听到“绿带”和“黑带”这两个术语。那么&#xff0c;究竟什么是绿带和…

现代挖掘机vr在线互动展示厅是实现业务增长的加速度

VR数字博物馆全景展示充分应用5G、VR全景、web3d开发和三维动画等技术&#xff0c;将实体博物馆整体还原到3D数字空间&#xff0c;让游客360全景漫游式参观&#xff0c;无论大小、贵重、破损的典藏展品都能通过3D建模技术&#xff0c;逼真重现到三维虚拟场景中&#xff0c;让参…

VSCode汉化设置

扩展中搜索并安装 Chinese… 快捷键 commandshiftp&#xff0c;输入框中输入config&#xff0c;选择Config Display Language 选择zh-cn&#xff0c;重启vscode

红米电脑硬盘剪切

Redmi R14 2023版固态硬盘剪切 工具准备操作结尾语 首先要说明&#xff0c;本文所说的操作不一定适合你的电脑&#xff0c;因为电子产品更新换代过快&#xff0c;你的硬盘不一定能剪切&#xff0c;在操作前一定要仔细观察硬盘的型号&#xff0c;是否为同款&#xff0c;我上了图…

HarmonyOS鸿蒙原生应用开发设计- 流转图标

HarmonyOS设计文档中&#xff0c;为大家提供了独特的流转图标&#xff0c;开发者可以根据需要直接引用。 开发者直接使用官方提供的流转图标内容&#xff0c;既可以符合HarmonyOS原生应用的开发上架运营规范&#xff0c;又可以防止使用别人的图标侵权意外情况等&#xff0c;减…

TikTok文化探索:热议时事与社会话题

在当今数字时代&#xff0c;社交媒体平台如TikTok已经成为了我们日常生活中不可或缺的一部分。它不仅仅是一个娱乐应用&#xff0c;也是一扇窥视世界、探讨时事和社会话题的窗户。本文将深入探讨TikTok如何成为文化探索的平台&#xff0c;热议时事和社会话题&#xff0c;以及它…

LLVM学习笔记(50)

4.1.4. DAG合并与合法化 来自SelectionDAGBuilder的SelectionDAG输出还不能进行指令选择&#xff0c;必须通过额外的转换——显示在上图。在指令选择前应用的遍序列如下&#xff1a; 匹配一组节点&#xff0c;在有利时使用更简单的构造来替换它们&#xff0c;DAG合并遍优化Se…

基于NB-iot技术实现财物跟踪的EA01-SG定位模块方案

NB-iot无线数传模块可做财物防盗窃器&#xff0c;让你的财物可定位跟踪&#xff01; 随着社会的发展&#xff0c;公共资源及共享资源的蓬勃发展&#xff0c;对资产管理和资产追踪有了新的需求&#xff0c;如&#xff1a;某儿童玩具车在商场外面提供车辆乘坐游玩服务&#xff0…

一、python基础语法

文章目录 1.Python介绍1&#xff09;发明者龟叔2&#xff09;python特点3&#xff09;python缺点4&#xff09;Python版本 2.Python解释器1&#xff09;编译型和解释型2&#xff09;常见的python解释器3&#xff09;下载Python解释器4&#xff09;安装Python解释器5&#xff09…