【C++】stl_list介绍和实现,list和vector区别,list vector string 迭代器失效

news2024/11/18 13:39:27

本篇博客详细介绍list的实现&细节讲解,并且在文章末对list和vector,string进行区分和复习 

list的基本结构就是双向带头循环链表,链表和顺序表的差别我们在前面数据结构的时候早就学过了,不再赘述 

在使用stl库里面list时,要加上头文件

快速高效编辑查找元素 用vector

大批量增删数据 用list

目录

 1.基本框架的实现

2.很细节的函数实现 

3.vector和list对比

4.迭代器失效


1.基本框架的实现

定义模板T  还是表示每个节点data的类型

首先我们需要思考:

这个链表的每个节点的类型是什么?_list_node<T>

节点里面包含什么?当然 不要忘记构造函数

 list的成员类型基本上都是迭代器,当然链表都是在用指针在玩,这里的迭代器和我们之前写顺序表就很不一样,因为之前是连续存储的结构,可以用[ ]的方式,所以迭代器是原生指针,数组的结构正好支持迭代器行为

但是这里原生指针的类型是node* ,不能满足迭代器的行为

但是我们可以用封装+运算符重载搞定

 

 我们实现最基本的push——back()功能之后,基本上的框架就搭好了

void push_back(const T& x)
			{
				node* newnode  = new node(x);
				node* tail = head->_prev;
				//head  tail newnode
				tail->_next = newnode;
				newnode->_next = head;
				newnode->_prev = tail;
				head->_prev = newnode;

			}

目前的代码

namespace wrt
{
	template <typename T>
		struct _list_node
	{
			_list_node<T>* _prev;
			_list_node<T>* _next;
			T data;
			_list_node(const T& x)  //用x初始化节点
				:_prev(nullptr)
				,_next(nullptr)
				,data(x)
			{
			}
	};
		template <class T>
			struct __list_iterator
		{
			typedef _list_node<T> node;
			node* _pnode;
			__list_iterator(node* p)
				:_pnode(p)
			{}
			T& operator*()
			{
				return _pnode->data;
			}
			__list_iterator<T>& operator++()
			{
				_pnode = _pnode->_next;
				return *this;
			}
			bool operator!=(const __list_iterator<T>& it)
			{
				return _pnode != it._pnode;
			}
		};

		template <typename T>

		class list
		{
			typedef _list_node<T> node;
		public:
			typedef __list_iterator<T> iterator;
			iterator begin()
			{
				return iterator(head->_next);
			}
			iterator end()
			{
				return iterator(head);
			}
			list()
			{
				head = new node(T());
				head->_next = head;
				head->_prev = head;
			}
			void push_back(const T& x)
			{
				node* newnode  = new node(x);
				node* tail = head->_prev;
				//head  tail newnode
				tail->_next = newnode;
				newnode->_next = head;
				newnode->_prev = tail;
				head->_prev = newnode;

			}
		private :
			node* head;
		};
		void test()
		{
			list<int>  lt;
			lt.push_back(1);
			lt.push_back(3);
			lt.push_back(4);
			lt.push_back(5);
			lt.push_back(6);
			lt.push_back(7);

			list<int>::iterator it = lt.begin();
			while (it != lt.end())
			{
				cout << *it <<" ";
			++it;
			}
			cout <<endl;
		}
}

2.值得注意的函数

list为什么要支持sort,不可以使用algorithm文件里面的吗

 

 算法库里面使用了first-last 但是很显然list是不支持节点相减的


2.很细节的函数实现 

一个合格的list是支持增删查改的

push_back()已经实现过了

下面看insert()

 注意到它是有返回值的 ,返回迭代器,在头部增加数据当然是可以的

iterator insert(iterator  pos, const T& x)
			{
				node* newnode = new node(x);
				node* cur = pos._pnode;
				node* prev = cur->_prev;
				//prev newnode cur
				prev->_next = newnode;
				newnode->_next = cur;
				newnode->_prev = prev;
				cur->_prev = newnode;
				return iterator(newnode);
			}

 对应的erase()

 也是有返回值,并且不能在头结点位置删除,哨兵位不能动

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

那么头尾的增删就可以复用啦

void pop_back()
			{
				erase(--end());
			}
void pop_front()
			{
				erase(begin());
			}
void push_back(const T& x)
			{
				//node* newnode  = new node(x);
				//node* tail = head->_prev;
				head  tail newnode
				//tail->_next = newnode;
				//newnode->_next = head;
				//newnode->_prev = tail;
				//head->_prev = newnode;
				insert(end(), x);
			}
void push_front(const T& x)
			{
				insert(begin(), x);
			}

然后就是clear(),注意头结点不能删

	void clear()
			{
				iterator it = begin();
				while(it!=end())
				{
					it=erase(it);
				}
				//头节点不能删除
			}

析构函数

~list()
			{
				clear();
				//此时需要把头节点也删除
				delete head;
				head = nullptr;
			}

下面是拷贝构造

//拷贝构造
//l2=l1
			list<T>& operator=(const list<T>& l)
			{
				if (*this != l)
				{
					clear();
					for (const auto&e :l)
					{
						push_back(e);
					}
				}
				return *this;
			}

为了写着更方便,把头结点的开辟封装成函数

void empty_initialize()
			{
				head = new node(T());
				head->_next = head;
				head->_prev = head;
			}
//l2(l1)
			list(const list <T>& l)
			{
				empty_initialize();
				for (const auto& e : l)
				{
					push_back(e);
				}
			}

迭代器还有const版本怎么实现

最简单想到的就是直接在iterator类里面加上一个const修饰*运算符重载

template <class T>
			struct __list_iterator
		{
			typedef _list_node<T> node;
			node* _pnode;
			__list_iterator(node* p)
				:_pnode(p)
			{}
			T& operator*()
			{
				return _pnode->data;
			}
			//在同一个类里面实现就是不行,因为const——iterator只能遍历,不能++
		/*	const T& operator*() const 
			{
				return _pnode->data;
			}*/
			__list_iterator<T>& operator++()
			{
				_pnode = _pnode->_next;
				return *this;
			}
			bool operator!=(const __list_iterator<T>& it)
			{
				return _pnode != it._pnode;
			}
		};

但是这个真的对么?很显然不对,因为iterator可以遍历,++  但是const_iterator只能遍历无法++

所以很自然想到写在两个类里面

template <class T>
			struct __list_iterator
		{
			typedef _list_node<T> node;
			node* _pnode;
			__list_iterator(node* p)
				:_pnode(p)
			{}
			T& operator*()
			{
				return _pnode->data;
			}
			//在同一个类里面实现就是不行,因为const——iterator只能遍历,不能++
		/*	const T& operator*() const 
			{
				return _pnode->data;
			}*/
			__list_iterator<T>& operator++()
			{
				_pnode = _pnode->_next;
				return *this;
			}
			bool operator!=(const __list_iterator<T>& it)
			{
				return _pnode != it._pnode;
			}
		};
			template <class T>
			struct __list_const_iterator
			{
				typedef _list_node<T> node;
				node* _pnode;
				__list_const_iterator(node* p)
					:_pnode(p)
				{}
				const T& operator*() const 
				{
					return _pnode->data;
				}
				__list_const_iterator<T>& operator++()
				{
					_pnode = _pnode->_next;
					return *this;
				}
				bool operator!=(const __list_const_iterator<T>& it)
				{
					return _pnode != it._pnode;
				}
			};

 这两个类只在*运算符重载 还有名称上有区别

但是这是我们的想法,看一下源码就知道大佬果然是大佬

直接用两个模板参数解决问题

		template <typename T, typename Ref>
			struct __list_iterator
		{
			typedef _list_node<T> node;
			typedef __list_iterator<T, Ref> Self;
			node* _pnode;
			__list_iterator(node* p)
				:_pnode(p)
			{}
			Ref operator*()
			{
				return _pnode->data;
			}
			//在同一个类里面实现就是不行,因为const——iterator只能遍历,不能++
		/*	const T& operator*() const 
			{
				return _pnode->data;
			}*/
			Self& operator++()
			{
				_pnode = _pnode->_next;
				return *this;
			}
			Self& operator--()
			{
				_pnode = _pnode->_prev;
				return *this;
			}
			bool operator!=(const Self& it)
			{
				return _pnode != it._pnode;
			}
		};

拷贝构造可有现代写法哦,此时同样需要一个构造函数用迭代器初始化的

l2(l1)相比较来说,现代写法就是多一个工具人帮助复刻l1,然后把数据交换给l2,最后他自己牺牲....

首先是构造函数(用迭代器实现的)

template <class InputIterator>
			list(InputIterator first, InputIterator last)
				{
					empty_initialize();
			    while(first != last)
			    {
				push_back(*first);
				++first;
			    }
				}

 然后是swap

	void swap(const list<T>& l)
				{
					std::swap(head, l.head); //两个链表交换只需交换头结点
				}

现代写法

//l2(l1)
				list(const list<T>& l)
				{
					empty_initialize();
					list<T> tmp(l.begin(), l.end());
					swap(tmp); //tmp出作用域销毁
				}

 l2=l1   这是对于一个已经存在的对象l1,无需构造头结点

//l2=l1  这是对于一个已经存在的对象l1,无需构造头结点
				list <T>& operator=(const list<T>& l)
				{
					swap(l);
					return *this;
				}

想象一个场景:你需要统计size(),当然这种操作不能频繁进行,因为每一次都要从头开始遍历有很多消耗,那么最简单的办法是什么?!成员变量加上size

 现在凸显出复用的好处了,我虽然实现到一半开始想加上size,也只需要改动几个函数就可以完成功能

 

 

 

 

 其他全是复用,爽歪歪


可以用size实现两个函数

             size_t _size()
			{
				return size;
			}
			bool rmpty()
			{
				//return head->next==head;
				return size==0;
			}

C++兼容c是有前置和后置的(区分于有些语言,觉得前置后置很麻烦就删去后置)

完善一下前面对于迭代器的运算符操作

	//前置
			Self& operator++()
			{
				_pnode = _pnode->_next;
				return *this;
			}
			//后置
			Self& operator++(int)
			{
				Self tmp(*this);
				_pnode = _pnode->_next;
				return tmp;
			}
			//前置
			Self& operator--()
			{
				_pnode = _pnode->_prev;
				return *this;
			}
			//后置
			Self& operator--(int )
			{
				Self tmp(*this);
				_pnode = _pnode->_prev;
				return tmp;
			}

看起来写成这样是不是很完美,但是看一个问题

struct Pos
		{
			size_t _row;
			size_t _col;
			Pos(size_t row=0,size_t col=0)  //一定要时刻记着写一个默认构造!!!!!!
				:_row(row)
				,_col(col)
			{}
		};
		void test()
		{
			list<Pos> lt;
			Pos p1(1, 1);
			lt.push_back(p1);
			lt.push_back(p1);
			lt.push_back(p1);
			lt.push_back(Pos(2, 2)); //匿名函数
			lt.push_back(Pos(3, 3));
			list<Pos>::iterator it = lt.begin();
			while (it != lt.end())
			{
				//it->_row++;
				cout << *it << " ";
			}
		}

思考一下有什么问题???

 这很尴尬,首先我们思考一下,为什么C++支持cout,因为可以对内置类型可以直接识别然后输出,但是这里的it是个迭代器

其实这样就可以啦,只需要重载一个->这个运算符

但是我们只有T这个模板类型,没有T*

 

 

 然后运算符->重载这样写

Ptr operator->()
			{
				return &_pnode->data;
			}

注意:脑子一定要清醒,我们提供类T是为了list每个节点的数据类型,Ref是T&(当然还有一个const T&),Ptr是T*(还有const T*)

这里面也体现出我们typedef的智慧

这个模板我们改了很多次,但是我typedef之后,直接修改类型,不需要改名字,都是Self!!!

所以直接->访问就可以啦

他的原理就是

 


3.vector和list对比

 

4.迭代器失效

vector:insert和erase都有失效问题

lsit:erase会失效

那么string会有失效问题吗?当然,insert和erase都有,和vecor类似,但是一般不关注string失效,因为string的insert和erase常用接口都是下标支持的,迭代器用的少

 

 


最后我们的list实现总代码 

.h文件

#pragma once
namespace wrt
{
	template <typename T>
		struct _list_node
	{
			_list_node<T>* _prev;
			_list_node<T>* _next;
			T data;
			_list_node(const T& x)  //用x初始化节点
				:_prev(nullptr)
				,_next(nullptr)
				,data(x)
			{
			}
	};
		template <typename T, typename Ref,class Ptr>
			struct __list_iterator
		{
			typedef _list_node<T> node;
			typedef __list_iterator<T, Ref,Ptr> Self;
			node* _pnode;
			__list_iterator(node* p)
				:_pnode(p)
			{}
			Ref operator*()
			{
				return _pnode->data;
			}
			Ptr operator->()
			{
				return &_pnode->data;
			}
			//在同一个类里面实现就是不行,因为const——iterator只能遍历,不能++
		/*	const T& operator*() const 
			{
				return _pnode->data;
			}*/
			//前置
			Self& operator++()
			{
				_pnode = _pnode->_next;
				return *this;
			}
			//后置
			Self& operator++(int)
			{
				Self tmp(*this);
				_pnode = _pnode->_next;
				return tmp;
			}
			//前置
			Self& operator--()
			{
				_pnode = _pnode->_prev;
				return *this;
			}
			//后置
			Self& operator--(int )
			{
				Self tmp(*this);
				_pnode = _pnode->_prev;
				return tmp;
			}
			bool operator!=(const Self& it)
			{
				return _pnode != it._pnode;
			}
		};
		/*	template <class T>
			struct __list_const_iterator
			{
				typedef _list_node<T> node;
				node* _pnode;
				__list_const_iterator(node* p)
					:_pnode(p)
				{}
				const T& operator*() const 
				{
					return _pnode->data;
				}
				__list_const_iterator<T>& operator++()
				{
					_pnode = _pnode->_next;
					return *this;
				}
				bool operator!=(const __list_const_iterator<T>& it)
				{
					return _pnode != it._pnode;
				}
			};*/
			template <typename T>

		class list
		{
			typedef _list_node<T> node;
		public:
			//typedef __list_iterator<T> iterator;
			//typedef __list_const_iterator<T>  const_iterator;
			typedef __list_iterator<T,T&,T*> iterator;
			typedef __list_iterator<T,const T&,T*>  const_iterator;
			size_t _size()
			{
				return size;
			}
			bool rmpty()
			{
				//return head->next==head?
				return size == 0 ;
			}
			iterator begin()
			{
				return iterator(head->_next);
			}
			iterator end()
			{
				return iterator(head);
			}
			const_iterator begin() const 
			{
				return iterator(head->_next);
			}
			const_iterator end() const 
			{
				return iterator(head);
			}
			
			void push_back(const T& x)
			{
				//node* newnode  = new node(x);
				//node* tail = head->_prev;
				head  tail newnode
				//tail->_next = newnode;
				//newnode->_next = head;
				//newnode->_prev = tail;
				//head->_prev = newnode;
				insert(end(), x);
			}
			void push_front(const T& x)
			{
				insert(begin(), x);
			}
			~list()
			{
				clear();
				//此时需要把头节点也删除
				delete head;
				head = nullptr;
				size = 0;
			}
			
			//拷贝构造
			//l2=l1
			/*list<T>& operator=(const list<T>& l)
			{
				if (*this != l)
				{
					clear();
					for (const auto&e :l)
					{
						push_back(e);
					}
				}
				return *this;
			}*/
			void empty_initialize()
			{
				head = new node(T());
				head->_next = head;
				head->_prev = head;
				size = 0;
			}
			list()
			{
				empty_initialize();
			}
			//l2(l1)
		/*	list(const list <T>& l)
			{
				empty_initialize();
				for (const auto& e : l)
				{
					push_back(e);
				}
			}*/
			//拷贝构造的现代写法
			template <class InputIterator>
			list(InputIterator first, InputIterator last)
				{
					empty_initialize();
			    while (first != last)
			    {
				push_back(*first);
				++first;
			    }
				}
				void swap(const list<T>& l)
				{
					std::swap(head, l.head); //两个链表交换只需交换头结点
				}
				//l2(l1)
				list( list<T>& l)
				{
					empty_initialize();
					list<T> tmp(l.begin(), l.end());
					swap(tmp); //tmp出作用域销毁
				}
				//l2=l1  这是对于一个已经存在的对象l1,无需构造头结点
				list <T>& operator=(const list<T>& l)
				{
					swap(l);
					return *this;
				}
				void clear()
			{
				iterator it = begin();
				while(it!=end())
				{
					it=erase(it);
				}
				//头节点不能删除
				size = 0;
			}
			void pop_back()
			{
				erase(--end());
			}
			void pop_front()
			{
				erase(begin());
			}

			iterator insert(iterator  pos, const T& x)
			{
				node* newnode = new node(x);
				node* cur = pos._pnode;
				node* prev = cur->_prev;
				//prev newnode cur
				prev->_next = newnode;
				newnode->_next = cur;
				newnode->_prev = prev;
				cur->_prev = newnode;
				++size;
				return iterator(newnode);
			}
			iterator erase(iterator pos)
			{
				assert(pos != end());
				node* cur = pos._pnode;
				node* prev = cur->_prev;
				node* next = cur->_next;
				//prev cur next
				prev->_next = next;
				next->_prev = prev;
				delete cur;
				--size;
				return  iterator(next);
			}

		private :
			node* head;
			size_t size;
		};
		struct Pos
		{
			size_t _row;
			size_t _col;
			Pos(size_t row=0,size_t col=0)  //一定要时刻记着写一个默认构造!!!!!!
				:_row(row)
				,_col(col)
			{}
		};
		void test()
		{
			list<Pos> lt;
			Pos p1(1, 1);
			lt.push_back(p1);
			lt.push_back(p1);
			lt.push_back(p1);
			lt.push_back(Pos(2, 2)); //匿名函数
			lt.push_back(Pos(3, 3));
			list<Pos>::iterator it = lt.begin();
			while (it != lt.end())
			{
			//	cout << *it << " ";
				//cout << it.operator->()->_row << ":" << it->_col << endl;
				cout << it->_row << ":" << it->_col << ":" << endl;
			}
		}
		/*void test()
		{
			list<int>  lt;
			lt.push_back(1);
			lt.push_back(3);
			lt.push_back(4);
			lt.push_back(5);
			lt.push_back(6);
			lt.push_back(7);
			lt.insert(lt.begin(), 5);
			lt.erase(lt.begin());
			lt.push_back(40);
			list<int>::iterator it = lt.begin();
			while (it != lt.end())
			{
				cout << *it <<" ";
			++it;
			}
			cout <<endl;
			cout << lt._size() << endl;
		}*/
}

 .cpp文件

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <list>
#include <assert.h>
using namespace std;
#include "标头.h"
int main()
{
	wrt::test();

	return 0;
}

 

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

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

相关文章

3.1 第一个外设GPIO GPIO输出

GPIO简介•GPIO是通用输入输出口&#xff08;俗称IO口&#xff09;•可配置为8种输入输出模式•引脚电平&#xff1a;0V~3.3V&#xff08;数据0是0V&#xff0c;数据1是3.3V&#xff09;&#xff0c;部分引脚可容忍5V.•输出模式可控制端口输出高低电平&#xff0c;用以驱动LED…

Javascript周学习小结(初识,变量,数据类型)

JS的三大书写方式行内式如图所示&#xff1a;几点说明&#xff1a;JS的行内式写在HTML的标签内部&#xff0c;(常以on开头)&#xff0c;如onclick行内式常常使用单引号括住字符串以区分HTML的双引号可读性差&#xff0c;不建议使用引号易出错&#xff0c;不建议使用特殊情况下使…

【冲刺蓝桥杯的最后30天】day2

大家好&#x1f603;&#xff0c;我是想要慢慢变得优秀的向阳&#x1f31e;同学&#x1f468;‍&#x1f4bb;&#xff0c;断更了整整一年&#xff0c;又开始恢复CSDN更新&#xff0c;从今天开始更新备战蓝桥30天系列&#xff0c;一共30天&#xff0c;如果对你有帮助或者正在备…

Java代码是如何被CPU狂飙起来的?

&#x1f4e3;&#x1f4e3;&#x1f4e3;&#x1f4e3;&#x1f4e3;&#x1f4e3;&#x1f4e3; &#x1f38d;大家好&#xff0c;我是慕枫 &#x1f38d;前阿里巴巴高级工程师&#xff0c;InfoQ签约作者、阿里云专家博主&#xff0c;一直致力于用大白话讲解技术知识 &#x…

Stable Diffusion WebUI安装instruct-pix2pix插件

instruct-pix2pix作者团队提出了一种通过人类自然语言指令编辑图像的方法。他们的模型能够接受一张图像和相应的文字指令(也就是prompt)&#xff0c;根据指令来编辑图像。作者团队使用两个预训练模型&#xff08;一个是语言模型GPT-3, 另一个是文本到图像模型Stable Diffusion)…

Python---正则表达式

专栏&#xff1a;python 个人主页&#xff1a;HaiFan. 专栏简介&#xff1a;Python在学&#xff0c;希望能够得到各位的支持&#xff01;&#xff01;&#xff01; 正则表达式前言概念作用和特点使用场景正则符号re模块re.compile()match()search()span()findall()group()sub()…

uniapp系列-报错或常见问题处理集锦

问题一&#xff1a;执行完命令&#xff0c;就不动了&#xff0c;或者是uniapp 遇到编译很慢&#xff0c;无法正常运行的情况 情况1&#xff1a;执行run dev命令后&#xff0c;一直就不动了&#xff0c;输出如下 PS C:\XXXXXXXX> npx yarn run dev:h5 yarn run v1.22.19 wa…

YOLOv7实验记录

这篇博客主要记录博主在做YOLOv7模型训练与测试过程中遇到的一些问题。 首先我们需要明确YOLO模型权重文件与模型文件的使用 其实在github的readme中已经告诉我们使用方法&#xff0c;但我相信有很多像博主一样眼高手低的人可能会犯类似的错误。 训练 首先是训练时的设置&…

Object.defineProperty() 详解

一、对象的定义与赋值 我们经常使用的定义与赋值方法 obj.xxx value 或 obj[xxx] value&#xff0c;并且可以定义任意类型的值&#xff0c;如下所示&#xff1a; let obj {}; obj.name bjl; obj[age] 18; obj.sayHi function() {console.log(Hi)}; console.log(obj) /…

C语言自定义类型---进阶

之前的文章中有结构体初阶知识的讲解&#xff0c;对结构体不是很了解的小伙伴可以先去去看一下结构体初阶 结构体&#xff0c;枚举&#xff0c;联合结构体结构体类型的声明特殊的声明结构的自引用结构体变量的定义和初始化结构体内存对齐 <3 <3 <3(重点)那为什么存在内…

Unity记录2.1-动作-多段跳、蹬墙跳、墙体滑落

文章首发及后续更新&#xff1a;https://mwhls.top/4450.html&#xff0c;无图/无目录/格式错误/更多相关请至首发页查看。 新的更新内容请到mwhls.top查看。 欢迎提出任何疑问及批评&#xff0c;非常感谢&#xff01; 汇总&#xff1a;Unity 记录 摘要&#xff1a;实现跳跃、蹬…

若依学习(前后端分离版)——自定义注解@Log(如何自定义注解,实现aop)

如何自定义注解 aop的基本知识与应用 若依对用户的一些更新删除等敏感操作操作进行了日志记录 注解定义和切面处理的项目位置 第一步&#xff1a;自定义注解log 定义了注解的相关信息。这里定义的属性可以在使用时加以定义 注解Target和Retention的作用 第二步切面逻辑…

动手实现一遍Transformer

最近乘着ChatGpt的东风&#xff0c;关于NLP的研究又一次被推上了风口浪尖。在现阶段的NLP的里程碑中&#xff0c;无论如何无法绕过Transformer。《Attention is all you need》成了每个NLP入门者的必读论文。惭愧的是&#xff0c;我虽然使用过很多基于Transformer的模型&#x…

synchronized从入门到踹门

synchronized是什么synchronized是Java关键字&#xff0c;为了维护高并发是出现的原子性问题。技术是把双刃剑&#xff0c;多线程并发给我带来了前所未有的速率&#xff0c;然而在享受快速编程的过程&#xff0c;也给我们带来了原子性问题。如下&#xff1a;public class Main …

【微服务】认识微服务

目录 1.1 单体、分布式、集群 单体 分布式 集群 1.2 系统架构演变 1.2.1 单体应⽤架构 1.2.2 垂直应⽤架构 1.2.3 分布式架构 1.2.4 SOA架构 1.2.5 微服务架构 1.3 微服务架构介绍 微服务架构的常⻅问题 1.4 SpringCloud介绍 1.4.1 SpringBoot和SpringCloud有啥关…

[1.3_2]计算机系统概述——中断和异常

文章目录第一章 计算机系统概述中断和异常&#xff08;一&#xff09;中断的作用&#xff08;二&#xff09;中断的类型&#xff08;三&#xff09;中断机制的基本原理小结第一章 计算机系统概述 中断和异常 中断的作用中断的类型 内中断&#xff08;也称“异常”&#xff09;…

ES之DSL查询文档基础查询

分类 query查询分类 总体规律就是逻辑性的&#xff0c;从外层的你干嘛&#xff0c;到下一层的查询类型&#xff0c;再到下一层的查询字段&#xff08;如果需要的话&#xff09;和然后是查询内容 查询所有 语法 get /索引库名/_serarch {"query":{"查询条件…

【Linux】配置动态IP

动态IP 服务器重启完成之后&#xff0c;我们可以通过linux的指令 ip addr 来查询Linux系统的IP地址&#xff0c;具体信息如 下: 从图中我们可以看到&#xff0c;并没有获取到linux系统的IP地址&#xff0c;这是为什么呢&#xff1f;这是由于启动服务器时未 加载网卡&#x…

2D图像处理:Qt + Opencv使用光度立体法检测Halcon中提供的缺陷图像

文章目录 不需知道光源方向一、光度立体法(后续有时间在查资料研究)1.1 问题1:Slants和Tilts的理解(暂时是理解的)1.2 问题1:Gradient通道数为1,为何像素点对应的值会有两个?1.3 问题2:F(r,c)=(u(r,c),v(r,c)) 关于高斯曲率和平均曲率如何计算的?二、非标定光源实现光…

C++经典20题型,满满知识,看这一篇就够了(含答案)

今天找了20道c的经典题型&#xff0c;看这一篇就够了&#xff0c;全是干货 目录 1、题目&#xff1a;有一对兔子&#xff0c;从出生后第3个月起每个月都生一对兔子&#xff0c;小兔子长到第三个月后每个月又生一对兔子&#xff0c;假如兔子都不死&#xff0c;问每个月的兔子总…