用库造一个list的轮子 【C++】

news2024/9/25 21:27:43

文章目录

  • list的模拟实现
    • 默认成员函数
      • 构造函数
      • 拷贝构造函数
      • 赋值运算符重载
      • 析构函数
    • 迭代器
      • 迭代器为什么要存在?
      • const_iterator
      • begin和end
    • insert
    • erase
    • push_back && pop_back
    • push_front &&pop_front
    • swap
  • 完整代码

list的模拟实现

默认成员函数

构造函数

list是一个带头双向循环链表,在构造一个list对象时,new一个头结点,并让其prev和next都指向自己即可。

  	void empty_init()
		{
			_head = new Node;
			_head->_next = _head;
			_head->_prev = _head;
			_size = 0;

		}
	//默认构造
		list()
		{
			empty_init();
		}

拷贝构造函数

//拷贝构造函数
list(const list<T>& lt)
{
	_head = new node; //申请一个头结点
	_head->_next = _head; //头结点的后继指针指向自己
	_head->_prev = _head; //头结点的前驱指针指向自己
	for (auto & e : lt) //两个 e都是同一个
	{
		push_back(e); //将容器lt当中的数据一个个尾插到新构造的容器后面
	}
}

赋值运算符重载

版本一(推荐):
参数不使用引用,让编译器自动调用list的拷贝构造函数构造出来一个list对象,然后调用swap函数将原容器与该list对象进行交换

这样做相当于将应该用clear清理的数据,通过交换函数交给了容器lt,而当赋值运算符重载函数调用结束时,容器lt会自动销毁,并调用其析构函数进行清理。


		list<T> & operator= (list<T>  lt)//右值没有引用传参,间接调用拷贝构造
			//list<T>& operator= (  list<T> * this, list<T>  lt)//右值没有引用传参,间接调用拷贝构造
			// lt1 = lt2
		{
			this->swap(lt);
			return *this; 
		  }

版本二:
先调用clear函数将原容器清空,然后将容器lt当中的数据,通过遍历的方式一个个尾插到清空后的容器当中即可。

  
list<T>& operator=(const list<T>& lt)
{
	if (this != &lt) //避免自己给自己赋值
	{
		clear(); //清空容器
		for (const auto& e : lt)
		{
			push_back(e); //将容器lt当中的数据一个个尾插到链表后面
		}
	}
	return *this; //支持连续赋值
}

析构函数

对对象进行析构时,首先调用clear函数清理容器当中的数据,然后将头结点释放,最后将头指针置空

	 	void clear()
		{
			iterator it = begin();
			while (it!= end() )	
			{
				it = erase(it);

			}
			_size = 0;
		}

		~list()
		{
			clear();
			delete _head;
			_head = nullptr;
		}

迭代器

迭代器为什么要存在?

string 和vector的迭代器

string和vector将数据存储在一段连续的内存空间,那么可以通过指针进行自增、自减以及解引用等操作,就可以对相应位置的数据进行一系列操作,所以string和vector是天然的迭代器

在这里插入图片描述
list的迭代器
list中各个结点在内存当中的位置是随机的,不一定是连续的,我们不能仅通过结点指针的自增、自减以及解引用等操作对相应结点的数据进行操作 ,采用类封装迭代器,在迭代器类的内部,重载 ++ 、 --、 *、 -> 、 !=、 == 这些迭代器会用到的运算符

在这里插入图片描述

const_iterator

在const迭代器中,const迭代器指向的内容不能被修改。也就是解引用返回的值不能被修改。迭代器本身是可以修改的,有两种解决方案 :
1 再封装一个const迭代器类

	template< class T>
	//const 迭代器 ,让迭代器指向的内容不能修改, 迭代器本身可以修改
	struct __list_const_iterator
	{
		typedef list_node<T>  Node;

		//构造函数
		__list_const_iterator(Node* node)
			:_node(node)
		{

		}

		const T& operator*()//出了作用域,节点的值还在,用引用
			//const: 返回节点的值,不能修改
		{
			return _node->_val;
		}

		//前置++,返回++之后的值
		__list_const_iterator& operator++()
			//__list_const_iterator& operator++(__list_const_iterator  * this  )
		{
			_node = _node->_next;
			return *this;
		}
		//后置++ ,返回++之前的值
		__list_const_iterator operator++(int)
		{
			__list_const_iterator tmp(*this);
			_node = _node->_next;
			return tmp;// tmp出了作用域就被销毁 ,用传值返回 
		}

		bool operator==(const __list_iterator<T>& it)
		{
			return *this == it._node;
		}
		bool operator!=(const __list_iterator<T>& it)//传值返回,返回的是拷贝,是一个临时对象,临时对象具有常性
		{
			return *this != it._node;
		}
		Node* _node;
	};

2 选择增加模板参数,复用代码(推荐)

template<class T, class Ref, class Ptr>

在这里插入图片描述

c++库就是用的这种解决方案

	//template<class T> //list类存储的数据是任意类型,所以需要设置模板参数
	//普通迭代器
	//Ref是引用 ,Ptr是指针
	template<class T,class Ref,class Ptr>
	struct  __list_iterator
	{
		typedef list_node<T> Node;
		typedef __list_iterator<T, Ref, Ptr>  self;

		//构造函数
		__list_iterator(Node* node)
			:_node(node)
		{

		}

		Ref operator*()
		{
			return _node->_val;
		}

		Ptr operator->()
		{
			return &_node->_val;
		}

		//前置++,返回++之后的值
		self & operator++()
			//__list_iterator<T> & operator++(__list_iterator<T> * this  )
		{
			_node = _node->_next;
			return *this;

		}
		//后置++ ,返回++之前的值
	      self  operator++(int)
			//	__list_iterator<T> operator++( __list_iterator<T> * this ,int)
		{
			self tmp(*this);//拷贝构造
			_node = _node->_next;
			return tmp; // tmp出了作用域就被销毁 ,用传值返回 
		}
		bool operator!= (const self& it)
		{
			return _node != it._node;
		}
		bool operator== (const self & it)
		{
			return _node == it._node;
		}
		Node* _node;
	};
	template<class T>//list类存储的数据是任意类型,所以需要设置模板参数
	class list
	{
		typedef list_node<T>  Node;
	public:
		typedef  __list_iterator<T ,T&,T* >  iterator;
		typedef  __list_iterator<T, const T&, const T * >  const_iterator;
		//迭代器 
		//能直接显示构造最好显式构造,不要把决定权给编译器进行单参数的隐式类型转换
		iterator end()  //最后一个数据的下一个位置,即头节点
		{
			//return _head; // _head的类型是list_node<T>* ,iterator的类型是__list_iterator<T> ,类型不一致,涉及到单参数的构造函数支持隐式类型转换 
			//还可以写成 return iterator(_head);
			return iterator(_head);
		}
		iterator begin()//第一个数据的位置,即头节点的下一个位置
		{
			//return _head->_next;//单参数的构造函数支持隐式类型转换
			//还可以写成 return iterator(_head->_next)
			return iterator(_head->_next);
		}

		const_iterator begin() const
		{
			return const_iterator(_head->_next);
		}

		const_iterator end() const
		{
			return const_iterator(_head);
		}
		//默认构造
		list()
		{
			empty_init();
		}
		// lt2(lt1)
		//还没有实现const_iterator
		list(const list<T>& lt)
		{
			empty_init();
			//拷贝数据
			for (auto & e :lt )//遍历lt
			{
				push_back(e);
			}
		}
		~list()
		{
			clear();
			delete _head;
			_head = nullptr;
		}
		void empty_init()
		{
			_head = new Node;
			_head->_next = _head;
			_head->_prev = _head;
			_size = 0;

		}
		void swap(list<T> & lt)
		{
			std:: swap(_head,lt._head );
			std::swap(_size, lt._size);
		}
	

		list<T> & operator= (list<T>  lt)//右值没有引用传参,间接调用拷贝构造
			//list<T>& operator= (  list<T> * this, list<T>  lt)//右值没有引用传参,间接调用拷贝构造
			// lt1 = lt2
		{
			this->swap(lt);
			return *this; 
		  }


		void clear()
		{
			iterator it = begin();
			while (it!= end() )	
			{
				it = erase(it);

			}
			_size = 0;
		}
		void push_back(const T& x)
		{
			insert(end(), x);//在最后一个数据的下一个位置插入
		}
		//pos位置之前插入
		iterator insert(iterator pos, const T& x)
		{
			Node* cur = pos._node;
			Node* prev = cur->_prev;
			Node* newnode = new Node(x);
			// prev newnode cur 链接关系
			prev->_next = newnode;
			newnode->_prev = prev;
			newnode->_next = cur;
			cur->_prev = newnode;
			++_size;
			return newnode;
		}
		iterator erase (iterator pos)

		{
			assert(pos != end());
			Node* cur = pos._node;
			Node* next = cur->_next;
			Node* prev = cur->_prev;
			//prev next 
			prev->_next = next;
			next->_prev = prev;

			delete cur;
			--_size;
			return next;
		}

		size_t size()
		{
			return _size;
		}
		void push_front( const T & x )//T可能是vector ,用引用,减少拷贝
		{
			insert(begin(),x);
		}
		void pop_back()
		{
			erase(--end());//end是最后一个数据的下一个位置,需要--,到达最后一个数据,这样才是尾删
		}
		void pop_front()
		{
			erase(begin());
		}
	private:
		Node* _head;
		size_t _size;
	};

当我们定义const对象时,会自动调用const修饰的迭代器。当调用const修饰的迭代器时,__list_iterator的模板参数就会实例化为const T&。实际上在实例化时,const和非const修饰的还是两个不同类,只不过是实例化的代码工作交给了编译器处理了。

begin和end

对于list,第一个有效数据的迭代器就是头结点后一个结点
begin函数返回的是第一个有效数据的迭代器,即头节点的下一个位置
end函数返回的是最后一个有效数据的下一个位置的迭代器,即头节点

		iterator end()  //最后一个数据的下一个位置,即头节点
		{
			return _head; // _head的类型是list_node<T>* ,iterator的类型是__list_iterator<T> ,类型不一致,涉及到单参数的构造函数支持隐式类型转换 
			//还可以写成 return iterator(_head);
		}
		iterator begin()//第一个数据的位置,即头节点的下一个位置
		{
			return _head->_next;//单参数的构造函数支持隐式类型转换
			//还可以写成 return iterator(_head->_next)
		}

const对象的begin函数和end函数

	const_iterator begin() const
		{
			return const_iterator(_head->_next);//返回使用头结点后一个结点
		}

		const_iterator end() const
		{
			return const_iterator(_head);//返回使用头结点
		}

insert

在这里插入图片描述

重新改变prev newnode cur 三者之间的链接关系

 	//pos位置之前插入
		iterator insert(iterator pos, const T& x)
		{
			Node* cur = pos._node;
			Node* prev = cur->_prev;
			Node* newnode = new Node(x);
			// prev newnode cur 链接关系
			prev->_next = newnode;
			newnode->_prev = prev;
			newnode->_next = cur;
			cur->_prev = newnode;
			++_size;
			return newnode;
		}

erase

在这里插入图片描述
改变prev和next之间的链接关系,然后释放cur

iterator erase (iterator pos)
		{
			assert(pos != end());
			Node* cur = pos._node;
			Node* next = cur->_next;
			Node* prev = cur->_prev;
			//prev next 
			prev->_next = next;
			next->_prev = prev;
            delete cur ;
			--_size;
			return next;
		}

push_back && pop_back

	void push_back(const T& x)
		{
			insert(end(), x);//在最后一个数据的下一个位置插入
		}
	void pop_back()
		{
			erase(--end());//end是最后一个数据的下一个位置,需要--,到达最后一个数据,这样才是尾删
		}

push_front &&pop_front

		void pop_front()
		{
			erase(begin());
		}
		void push_front( const T & x )//T可能是vector ,用引用,减少拷贝
		{
			insert(begin(),x);
		}

swap

swap函数用于交换两个容器,list容器当中存储的是链表的头指针和size,我们将这两个容器当中的头指针和size交换

		void swap(list<T> & lt)
		{
			std:: swap(_head,lt._head );
			std::swap(_size, lt._size);
		}

注意: 这里调用库里的swap模板函数,需要在swap函数之前加上“std::”,告诉编译器在c++标准库寻找swap函数,否则编译器编译时会认为你调用的是正在实现的swap函数(就近原则)

总结

在这里插入图片描述

完整代码

#pragma once
#include<iostream>
#include<assert.h>
#include<list>
using namespace std;
namespace cxq
{
	//list类存储的数据是任意类型,所以需要设置模板参数
	template<class T>
	//节点
	struct list_node
	{

		//构造函数
		list_node(const T& val = T()) //缺省值是匿名对象,c++对内置类型进行了升级
			:_prev(nullptr)
			, _next(nullptr)
			, _val(val)
		{
			
		}

		list_node<T>* _prev;
		list_node<T>* _next;
		T _val;
	};

	//template<class T> //list类存储的数据是任意类型,所以需要设置模板参数
	//普通迭代器
	//Ref是引用 ,Ptr是指针
	template<class T,class Ref,class Ptr>
	struct  __list_iterator
	{
		typedef list_node<T> Node;
		typedef __list_iterator<T, Ref, Ptr>  self;

		//构造函数
		__list_iterator(Node* node)
			:_node(node)
		{

		}

		Ref operator*()
		{
			return _node->_val;
		}

		Ptr operator->()
		{
			return &_node->_val;
		}

		//前置++,返回++之后的值
		self & operator++()
			//__list_iterator<T> & operator++(__list_iterator<T> * this  )

		{
			_node = _node->_next;
			return *this;

		}
		//后置++ ,返回++之前的值
	      self  operator++(int)
			//	__list_iterator<T> operator++( __list_iterator<T> * this ,int)

		{
			self tmp(*this);//拷贝构造
			_node = _node->_next;
			return tmp; // tmp出了作用域就被销毁 ,用传值返回 
		}
		bool operator!= (const self& it)
		{
			return _node != it._node;
		}
		bool operator== (const self & it)
		{
			return _node == it._node;
		}

		Node* _node;
	};


	//template< class T>
	const 迭代器 ,让迭代器指向的内容不能修改, 迭代器本身可以修改
	//struct __list_const_iterator
	//{
	//	typedef list_node<T>  Node;

	//	//构造函数
	//	__list_const_iterator(Node* node)
	//		:_node(node)
	//	{

	//	}

	//	const T& operator*()//出了作用域,节点的值还在,用引用
	//		//const: 返回节点的值,不能修改
	//	{
	//		return _node->_val;
	//	}

	//	//前置++,返回++之后的值
	//	__list_const_iterator& operator++()
	//		//__list_const_iterator& operator++(__list_const_iterator  * this  )
	//	{
	//		_node = _node->_next;
	//		return *this;
	//	}
	//	//后置++ ,返回++之前的值
	//	__list_const_iterator operator++(int)
	//	{
	//		__list_const_iterator tmp(*this);
	//		_node = _node->_next;
	//		return tmp;// tmp出了作用域就被销毁 ,用传值返回 
	//	}

	//	bool operator==(const __list_iterator<T>& it)
	//	{
	//		return *this == it._node;
	//	}
	//	bool operator!=(const __list_iterator<T>& it)//传值返回,返回的是拷贝,是一个临时对象,临时对象具有常性
	//	{
	//		return *this != it._node;
	//	}
	//	Node* _node;
	//};


	template<class T>//list类存储的数据是任意类型,所以需要设置模板参数
	class list
	{

		typedef list_node<T>  Node;
	public:
		typedef  __list_iterator<T ,T&,T* >  iterator;//普通迭代器
		typedef  __list_iterator<T, const T&, const T * >  const_iterator;//const 迭代器


		//迭代器 
		//能直接显示构造最好显式构造,不要把决定权给编译器进行单参数的隐式类型转换
		iterator end()  //最后一个数据的下一个位置,即头节点
		{
			//return _head; // _head的类型是list_node<T>* ,iterator的类型是__list_iterator<T> ,类型不一致,涉及到单参数的构造函数支持隐式类型转换 
			//还可以写成 return iterator(_head);
			return iterator(_head);
		}
		iterator begin()//第一个数据的位置,即头节点的下一个位置
		{
			//return _head->_next;//单参数的构造函数支持隐式类型转换
			//还可以写成 return iterator(_head->_next)
			return iterator(_head->_next);
		}

		const_iterator begin() const
		{
			return const_iterator(_head->_next);
		}

		const_iterator end() const
		{
			return const_iterator(_head);
		}


		//默认构造
		list()
		{
			empty_init();
		}
		// lt2(lt1)
		//还没有实现const_iterator
		list(const list<T>& lt)
		{
			empty_init();
			//拷贝数据
			for (auto & e :lt )//遍历lt
			{
				push_back(e);
			}
		}

		~list()
		{
			clear();
			delete _head;
			_head = nullptr;
		}
		void empty_init()
		{
			_head = new Node;
			_head->_next = _head;
			_head->_prev = _head;
			_size = 0;

		}
		void swap(list<T> & lt)
		{
			std:: swap(_head,lt._head );
			std::swap(_size, lt._size);
		}
	

		list<T> & operator= (list<T>  lt)//右值没有引用传参,间接调用拷贝构造
			//list<T>& operator= (  list<T> * this, list<T>  lt)//右值没有引用传参,间接调用拷贝构造
			// lt1 = lt2
		{
			this->swap(lt);
			return *this; 
		  }


		void clear()
		{
			iterator it = begin();
			while (it!= end() )	
			{
				it = erase(it);

			}
			_size = 0;
		}
		void push_back(const T& x)
		{
			找尾
			//Node* tail = _head->_prev;
			//Node* newnode = new Node(x);
			改变链接关系 
			///*newnode = tail->next;*/
			//tail->_next = newnode;
			//newnode->_prev = tail;

			//_head->_prev = newnode;
			//newnode->_next = _head;

			insert(end(), x);//在最后一个数据的下一个位置插入

		}
		//pos位置之前插入
		iterator insert(iterator pos, const T& x)
		{
			Node* cur = pos._node;
			Node* prev = cur->_prev;
			Node* newnode = new Node(x);
			// prev newnode cur 链接关系
			prev->_next = newnode;
			newnode->_prev = prev;
			newnode->_next = cur;
			cur->_prev = newnode;
			++_size;
			return newnode;
		}
		iterator erase (iterator pos)

		{
			assert(pos != end());
			Node* cur = pos._node;
			Node* next = cur->_next;
			Node* prev = cur->_prev;
			//prev next 
			prev->_next = next;
			next->_prev = prev;

			delete cur;
			--_size;
			return next;
		}

		size_t size()
		{
			return _size;
		}
		void push_front( const T & x )//T可能是vector ,用引用,减少拷贝
		{
			insert(begin(),x);
		}
		void pop_back()
		{
			erase(--end());//end是最后一个数据的下一个位置,需要--,到达最后一个数据,这样才是尾删
		}
		void pop_front()
		{
			erase(begin());
		}

	private:
		Node* _head;
		size_t _size;
	};
	void test_list1()
	{
		list<int> lt1;
		lt1.push_back(1);
		lt1.push_back(2);

		list<int>::iterator it = lt1.begin();//拷贝构造
		while (it != lt1.end())
		{
			cout << *it << " ";
			it++;
		}
		cout << endl;
	}
	void test_list2()
	{
		list<int> lt1;
		lt1.push_back(1);
		lt1.push_back(2);

		list<int> lt2 (lt1);
		for (auto e : lt1)
		{
			cout << e << " ";
		}
		cout << endl;
	}
}



如果你觉得这篇文章对你有帮助,不妨动动手指给点赞收藏加转发,给鄃鳕一个大大的关注你们的每一次支持都将转化为我前进的动力!!!

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

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

相关文章

SpringBoot 底层机制分析[上]

文章目录 分析SpringBoot 底层机制【Tomcat 启动分析Spring 容器初始化Tomcat 如何关联Spring 容器】[上]搭建SpringBoot 底层机制开发环境Configuration Bean 会发生什么&#xff0c;并分析机制提出问题&#xff1a;SpringBoot 是怎么启动Tomcat &#xff0c;并可以支持访问C…

ios启动崩溃保护

网传上个月下旬小红书因为配置问题导致连续性启动崩溃&#xff0c;最终只能通过紧急发版解决。对于冷启动崩溃保护的最容易查到的资料来源于微信读书团队的分享。 何为保护&#xff1f;要保护什么&#xff1f;该怎样保护&#xff1f;带着这几个疑问&#xff0c;一一谈一下个人的…

浅谈常态化压测

目录 一、常态化压测介绍 1.什么是常态化压测 2.为什么要进行常态化压测 3.常态化压测的价值 二、常态化压测实践 1.常态化压测流程介绍 2.首次进行常态化压测实践 2.1 准备阶段 2.2 执行阶段 2.3 调优阶段 2.4 复盘阶段 三、常态化压测总结 一、常态化压测介绍 1…

AI让分子“起死回生”:拯救抗生素的新希望

生物工程师利用人工智能(AI)使分子“起死回生”[1]。 为实现这种分子“复活”,研究人员应用计算方法对来自现代人类(智人)和我们早已灭绝的远亲尼安德特人和丹尼索瓦人的蛋白质数据进行分析。这使研究人员能够鉴定出可以杀死致病细菌的分子&#xff0c;从而促进研发用于治疗人类…

微信生态升级!小绿书来了!

如你所知&#xff0c;微信不只是一个聊天工具。一切从照片开始&#xff0c;你拍了一张照片&#xff0c;你就拥有了自己的相册&#xff0c;在“朋友圈”你可以了解朋友们的生活。如你所见&#xff0c;微信&#xff0c;是一个生活方式。不知不觉间&#xff0c;微信已经走过了 11个…

Docker的入门与使用

什么是Docker&#xff1f; docker官网 简介与概述 Docker 是一个开源的应用容器引擎&#xff0c;基于 Go 语言 并遵从 Apache2.0 协议开源。 Docker 可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中&#xff0c;然后发布到任何流行的 Linux 机器上&#x…

C字符串与C++ string 类:用法万字详解(上)

目录 引言 一、C语言字符串 1.1 创建 C 字符串 1.2 字符串长度 1.3 字符串拼接 1.4 比较字符串 1.5 复制字符串 二、C字符串string类 2.1 解释 2.2 string构造函数 2.2.1 string() 默认构造函数 2.2.2 string(const char* s) 从 C 风格字符串构造 2.2.3 string(co…

通讯协议034——全网独有的OPC HDA知识一之聚合(三)时间加权平均

本文简单介绍OPC HDA规范的基本概念&#xff0c;更多通信资源请登录网信智汇(wangxinzhihui.com)。 本节旨在详细说明HDA聚合的要求和性能。其目的是使HDA聚合标准化&#xff0c;以便HDA客户端能够可靠地预测聚合计算的结果并理解其含义。如果用户需要聚合中的自定义功能&…

使用一个python脚本抓取大量网站【2/3】

一、说明 我如何使用一个 Python 脚本抓取大量网站&#xff0c;在第 2 部分使用 Docker &#xff0c;“我如何使用一个python脚本抓取大量网站”统计数据。在本文中&#xff0c;我将与您分享&#xff1a; Github存储库&#xff0c;您可以从中克隆它;链接到 docker 容器&#xf…

软件定制开发平台:管好数据资源,降本提质!

在如今的发展时代&#xff0c;利用好优质的软件定制开发平台&#xff0c;定能给广大用户提高办公协作效率&#xff0c;创造可观的市场价值。作为服务商&#xff0c;流辰信息一直在低代码市场勤于钻研&#xff0c;不断努力&#xff0c;保持敏锐的市场眼光和洞察力&#xff0c;为…

Modelsim恢复编辑器的解决方案——只能将外部编辑器删除后,重新匹配编辑器

Modelsim恢复编辑器的解决方案——只能将外部编辑器删除后&#xff0c;重新匹配编辑器 1&#xff0c;Modelsim和Questasim是相互兼容的&#xff0c;配置的编辑器变成了sublime&#xff0c;且更换不了编辑器2&#xff0c;解决问题的方案&#xff0c;还是没得到解决3&#xff0c;…

Markdown和LaTex的学习

下载Typora Typora(免费版) 轻量级Markdown编辑器 - 哔哩哔哩 (bilibili.com) 部分编辑器需要进入设置 中开启特定的 Markdown 语法&#xff0c;例如 Typora 就需要手动开启 高亮 功能 Typora的使用&#xff1a; Typora中各种使用 - lyluoye - 博客园 (cnblogs.com) 标题 #…

数据库的存储过程、触发器、事件 实现(超精简)

一 存储过程 什么是存储过程 &#xff1a; 自己搜 和代码写的有什么区别&#xff1a; 没区别 为什么用存储过程&#xff1a; 快 例子 -- 创建 test名字的存储过程 CREATE PROCEDURE test(in idin INT) BEGIN-- 创建变量declare id int default 0;declare stopflag int defau…

爬虫015_python异常_页面结构介绍_爬虫概念介绍---python工作笔记034

来看python中的异常 可以看到不做异常处理因为没有这个文件所以报错了 来看一下异常的写法

【C++】C++回调函数基本用法(详细讲解)

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;公众号&#x1f448;&#xff1a;测试开发自动化【获取源码商业合作】 &#x1f449;荣__誉&#x1f448;&#xff1a;阿里云博客专家博主、5…

【Flutter】【packages】simple_animations 简单的实现动画

package&#xff1a;simple_animations 导入包到项目中去 可以实现简单的动画&#xff0c; 快速实现&#xff0c;不需要自己过多的设置 有多种样式可以实现[ ] 功能&#xff1a; 简单的用例&#xff1a;具体需要详细可以去 pub 链接地址 1. PlayAnimationBuilder PlayAnima…

winform控件 datagridview分页功能

主要实现页面跳转、动态改变每页显示行数、返回首末页、上下页功能&#xff0c;效果图如下&#xff1a; 主代码如下&#xff1a; namespace Paging {public partial class Form1 : Form{public Form1(){InitializeComponent();}private int currentPageCount;//记录当前页行数…

ApplicationContext在Spring Boot中是如何创建的?

一、ApplicationContext在Spring Boot中是如何创建的&#xff1f; 1. SpringApplication ApplicationContextFactory有三个实现类&#xff0c;分别是AnnotationConfigReactiveWebServerApplicationContext.Factory、AnnotationConfigServletWebServerApplicationContext.Facto…

nginx动态加载配置文件的方法

1. main函数调用ngx_get_options函数 2. ngx_get_options(int argc, char *const *argv)中会解析用户输入命令。 case ‘s’: if (*p) { ngx_signal (char *) p; } else if (argv[i]) {ngx_signal argv[i];} else {ngx_log_stderr(0, "option \"-s\" requi…

将数组按照某个对象分类,结果值的json的值按照value递增排序

const arr [ { value: 532, lable: 1, type: “a” }, { value: 132, lable: 24, type: “b” }, { value: 432, lable: 13, type: “b” }, { value: 1812, lable: 5, type: “b” }, { value: 1932, lable: 8, type: “c” }, { value: 132, lable: 4, type: “a” }, { val…