C++_list简单源码剖析:list模拟实现

文章目录

    • 🚀1. ListNode模板
    • 🚀2. List_iterator模板(重要)
      • 🌱2.1 List_iterator的构造函数
      • 🌱2.2 List_iterator的关于ListNode的行为
    • 🚀3. Reverse_list_iterator模板(拓展)
    • 🚀4. List模板(核心)
      • 🌱4.1 List构造函数
      • 🌱4.2 List迭代器
      • 🌱4.3 List内容相关函数
      • 🌱4.4 List获取头尾元素
      • 🌱4.5 List内存修改

大家好!本文会用C++模拟一个基本的list类,帮助我们更好的理解list的内置函数的实现与规则。在这里插入图片描述

list是带头双向循环链表,在C语言实现一个链表时,我们通常会实现两个结构体,List 与 ListNode,也就是封装了List的本体与List的节点,在C++模拟实现list也是如此,我们需要实现List与ListNode两个模板。

🚀1. ListNode模板

回忆C语言的链表节点封装,大致其实现为:

struct listnode
{
	listnode* next;
	listnode* prev;
	int val;
};

由此,ListNode的内容是比较简单,我们先行封装ListNode,以模板的形式实现,其成员变量只有一个val与两个指针:指针一个指向前节点,一个指向后节点,val就是存储的数据

template<class T>
struct ListNode
{
	ListNode<T>* _prev;
	ListNode<T>* _next;
	 
	T _val;
	//构造函数
	ListNode(const T& val = T()) :
		_prev(nullptr),
		_next(nullptr),
		_val(val)
	{

	};
};

备注:

  1. 关于使用struct的原因:在C++中,如果一个类的成员变量全都是需要公开被访问的,我们通常使用struct不用class。我们封装ListNode,实际上是在List类中进行操作的,为了List能更方便的实现ListNode访问上下节点的操作,ListNode的成员必须的public的。
    : struct默认的所有成员是public,class默认的所有成员是private。
  2. 关于 const T& val = T()在 C++_vector简单源码剖析:vector模拟实现 中⚡️内置类型也有构造函数 这里有说明,其实就是为了当T是内置类型(int , double)的时候,能顺利初始化val

🚀2. List_iterator模板(重要)

Listiterator就是list的迭代器,提问🎤:在list类的封装中,能不能直接这么定义迭代器?->

template<class T>
class List
{
public:
	typedef ListNode<T> Node;
	typedef Node* iterator;
}

📜答案:不能,为什么🤔?在 C++_vector简单源码剖析:vector模拟实现 中,实现vector的迭代器似乎就是这样实现的,为什么到了list就不行了?
💡解释:在这里插入图片描述
那要如何实现?既然默认的++或者–并不是我们所需要的,那么我们就要自己定义这些行为,这里就需要封装一个Listiterator模板,用一个类来封装ListNode与它的一些行为

//List的迭代器模板
template<class T, class str, class ptr>
struct Listiterator
{
	typedef ListNode<T> Node;
	typedef Listiterator<T, str, ptr> Self;
	Node* _node;

	//迭代器的构造函数
	Listiterator(Node* pNode = nullptr);
	Listiterator(const Self& l);
	
	// ListNode的一些行为
	//前置++重载,当前节点到下一个节点
	Self operator++();
	//后置++重载,当前节点到下一个节点
	Self operator++(int);
	//前置--重载,当前节点到上一个节点
	Self operator--();
	//后置--重载,当前节点到上一个节点
	Self& operator--(int);
	//解引用重载
	str operator*();
	//->重载
	ptr operator->();
	//!=重载
	bool operator!=(const Self& l);
	//==重载
	bool operator==(const Self& l);
};

📈备注:

  1. 在List模板中会实例化两个版本的迭代器,一个iterator,一个const_iterator, str会传入 T& 与 const T&,ptr会传入T与const T * 。
  2. Listiterator的内容只有一个成员变量ListNode和它的一些行为,这样的过程叫做封装

🌱2.1 List_iterator的构造函数

typedef ListNode<T> Node;
typedef Listiterator<T, str, ptr> Self;
	Node* _node;
//迭代器的构造函数
Listiterator(Node* pNode = nullptr){
	_node = pNode;
}
Listiterator(const Self& l) {
	_node = l._node;
}

📈备注:构造一个迭代器,让其成员ListNode = 传入的ListNode

🌱2.2 List_iterator的关于ListNode的行为

	typedef ListNode<T> Node;
	typedef Listiterator<T, str, ptr> Self;
	Node* _node;
//前置++重载,当前节点到下一个节点
Self operator++()
{
	_node = _node->_next;
	return *this;
};
//后置++重载,当前节点到下一个节点
Self operator++(int) {
	Node* tmp = _node;
	_node = _node->_next;
	return tmp;
};
//前置--重载,当前节点到上一个节点
Self operator--()
{
	_node = _node->_prev;
	return *this;
};
//后置--重载,当前节点到上一个节点
Self& operator--(int) {
	Node* tmp = _node;
	_node = _node->_prev;
	return tmp;
};
//解引用重载
str operator*()
{
	return _node->_val;
};
//->重载
ptr operator->()
{
	return &*this;
};
//!=重载
bool operator!=(const Self& l) {
	return l._node !=_node;
};
//==重载
bool operator==(const Self& l) {
	return l._node == _node;
};

📈备注:++让封装的ListNode指向下一个节点,–指向上一个节点, * 解引用得到当前节点的值,->通过this指针使用成员,比较相不相等就是比较是不是同个节点。

🚀3. Reverse_list_iterator模板(拓展)

Reverse_list_iterator就是反向迭代器。

//反向迭代器
template<class T, class str, class ptr>
struct Reverse_list_iterator
{
	typedef ListNode<T> Node;
	typedef Reverse_list_iterator<T, str, ptr> Self;
	Node* _node;

	//构造函数
	Reverse_list_iterator(Node* tmp):
		_node(tmp) {};

	//前置++重载
	Self operator++()
	{
		_node = _node->_prev;
		return *this;
	};
	//后置++重载
	Self operator++(int) {
		Node* tmp = _node;
		_node = _node->_prev;
		return tmp;
	};
	//前置--重载,
	Self operator--()
	{
		_node = _node->_next;
		return *this;
	};
	//后置--重载
	Self& operator--(int) {
		Node* tmp = _node;
		_node = _node->_next;
		return tmp;
	};
	//解引用重载
	str operator*()
	{
		return (_node->_prev)->_val;
	};
	//->重载sh
	ptr operator->()
	{
		return &*this;
	};
	//!=重载
	bool operator!=(const Self& l) {
		return l._node != _node;
	};
	//==重载
	bool operator==(const Self& l) {
		return l._node == _node;
	};

};

📉省流:与正常迭代器不同的地方就是 ++ , – ,和解引用 * , * 解引用获得的是上一个节点的值。

🚀4. List模板(核心)

List模板有很多的成员函数,我们先大致浏览一下它的成员:

	template<class T>
	class List
	{
	public:
		typedef ListNode<T> Node;
		//迭代器命名
		typedef Listiterator<T, T&, T*> iterator;
		typedef Listiterator<T, const T&, const T*> const_iterator;
		typedef Reverse_list_iterator<T, T&, T*> reverse_list_iterator;
		typedef Reverse_list_iterator<T, const  T&, const  T*> const_reverse_list_iterator;
		//1.  List的构造
		//默认构造
		List();
		//传值构造
		List(int n, const T& value = T());
		//迭代器构造
		template <class Iterator>
		List(Iterator first, Iterator last);
		//拷贝构造
		List(const List<T>& l);
		//赋值重载
		List<T>& operator=(List<T> l);
		//析构函数
		~List();


		//2. 迭代器
		iterator begin() ;
		iterator end();
		const_iterator begin() const;
		const_iterator end() const;
		reverse_list_iterator rbegin();
		reverse_list_iterator rend();
		const_reverse_list_iterator rbegin() const;
		const_reverse_list_iterator rend() const;

		//3. 内容相关
		size_t size()const;
		bool empty()const;

		//4. 获取头尾元素
		T& front();
		const T& front()const;
		T& back();
		const T& back()const;

		//5. 内存修改
		void push_back(const T& val);
		void pop_back();
		void push_front(const T& val);
		void pop_front();

		// 在pos位置前插入值为val的节点
		//不存在迭代器失效
		iterator insert(iterator pos, const T& val);

		// 删除pos位置的节点,返回该节点的下一个位置
		//存在迭代器失效
		iterator erase(iterator pos);

		void clear();
		void swap(List<T>& l);
	private: 
		Node* _pHead;
		void createhead()
		{
			_pHead = new Node;
			_pHead->_prev = _pHead;
			_pHead->_next = _pHead;
		}
	};
}

🌱4.1 List构造函数

//创造head节点
void createhead()
{
		_pHead = new Node;
		_pHead->_prev = _pHead;
		_pHead->_next = _pHead;
}
//默认构造
List()
{
	createhead();
}
//传值构造
List(int n, const T& value = T())
{
	createhead();
	for (int i = 0; i < n; ++i)
	push_back(value);
}
//迭代器构造
template <class Iterator>
List(Iterator first, Iterator last)
{
	createhead();
	auto start = first;
	while (start != last)
	{
		push_back(*start);
		start++;
	};
};
//拷贝构造
List(const List<T>& l)
{
	createhead();
	for (auto e : l)
	{
		push_back(e);
	}
};

//赋值重载
List<T>& operator=(List<T> l)
{
	swap(_pHead, l._pHead);
	return *this;
}
//析构函数
~List()
{
	clear();
	delete _pHead;
}

📈备注:

  1. 默认构造支持创建空List
  2. 传值构造支持创建长度为n,值为value的List
  3. 迭代器构造支持通过传入迭代器进行初始化的List.
  4. 拷贝构造和赋值重载支持传入一个List进行初始化
  5. 析构函数要先clear,这个clear就是清除所有节点的函数,后面会实现

🌱4.2 List迭代器

一般迭代器:

typedef ListNode<T> Node;
typedef Listiterator<T, T&, T*> iterator;
typedef Listiterator<T, const T&, const T*> const_iterator;

iterator begin() {
	return iterator(_pHead->_next);
};
iterator end()
{
	return iterator(_pHead);
};
const_iterator begin() const
{
	return const_iterator(_pHead->_next);
};
const_iterator end() const
{
	return const_iterator(_pHead);
};

📈备注:begin()返回头节点,end()返回尾节点的下一个节点(就是head节点)

反向迭代器:

typedef ListNode<T> Node;
typedef Reverse_list_iterator<T, T&, T*> reverse_list_iterator;
typedef Reverse_list_iterator<T, const  T&, const  T*> const_reverse_list_iterator;

reverse_list_iterator rbegin(){
	return reverse_list_iterator(_pHead);
};
reverse_list_iterator rend()
{
	return reverse_list_iterator(_pHead->_next);
};
const_reverse_list_iterator rbegin() const
{
	return const_reverse_list_iterator(_pHead);
};
const_reverse_list_iterator rend() const
{
	return const_reverse_list_iterator(_pHead->_next);
};

📈备注:begin()返回头节点前一个节点(就是head节点),end()返回尾节点

🌱4.3 List内容相关函数

size_t size()const
{
	size_t size = 0;
	auto it = begin();
	while (it != end())
	{
		size++;
		it++;
	}
	return size;
}
bool empty()const
{
	return size() == 0;
}

📈备注:size与empty函数慎用,时间复杂度为O(n)。

🌱4.4 List获取头尾元素

T& front()
{
	return (_pHead->_next)->_val;
}
const T& front()const
{
	return (_pHead->_next)->_val;
}
T& back()
{
	return (_pHead->_prev)->_val;
}
const T& back()const
{
	return (_pHead->_prev)->_val;
}

📈备注:注意要多实现一个const版本的。

🌱4.5 List内存修改

//5. 内存修改
void push_back(const T& val) {
	insert(end(), val);
};
void pop_back() {
	erase(--end());
};
void push_front(const T& val) {
	insert(begin(), val);
};
void pop_front() {
	erase(begin());
};

// 在pos位置前插入值为val的节点
//不存在迭代器失效
iterator insert(iterator pos, const T& val) {
	Node* tmp = new Node(val);
	Node* cur = pos._node;
	Node* prev = cur->_prev;

	tmp->_next = cur;
	tmp->_prev = prev;

	cur->_prev = tmp;
	prev->_next = tmp;

	return  iterator(tmp);
};

// 删除pos位置的节点,返回该节点的下一个位置
//存在迭代器失效
iterator erase(iterator pos)
{
	Node* Prew = pos._node->_prev;
	Node* Next = pos._node->_next;
	delete pos._node;
	Prew->_next = Next;
	Next->_prev = Prew;
	return Next;
};

void clear() { 
	iterator start = begin();
	while (start != end())
	{
		start = erase(start);
	}
	_pHead->_next = _pHead;
	_pHead->_prev = _pHead;
}
void swap(List<T>& l)
{
	Node tmp = _pHead;
	_pHead = l._pHead;
	l._pHead = tmp;
}

📈备注:insert传入的迭代器不会失效,但是erase传入的迭代器会失效,erase返回删除节点的下一个节点的有效迭代器。其他是内存修改函数都是通过复用insert与erase实现的。

由此全部完成,附上总代码图:

#pragma once
namespace bit
{
	//ListNode节点
	template<class T>
	struct ListNode
	{
		ListNode<T>* _prev;
		ListNode<T>* _next;
		 
		T _val;

		ListNode(const T& val = T()) :
			_prev(nullptr),
			_next(nullptr),
			_val(val)
		{

		};
	};

	

	
	//List的迭代器模板
	template<class T, class str, class ptr>
	struct Listiterator
	{
		typedef ListNode<T> Node;
		typedef Listiterator<T, str, ptr> Self;
		Node* _node;

		//迭代器的构造函数
		Listiterator(Node* pNode = nullptr){
			_node = pNode;
		}
		Listiterator(const Self& l) {
			_node = l._node;
		}

		//前置++重载,当前节点到下一个节点
		Self operator++()
		{
			_node = _node->_next;
			return *this;
		};
		//后置++重载,当前节点到下一个节点
		Self operator++(int) {
			Node* tmp = _node;
			_node = _node->_next;
			return tmp;
		};
		//前置--重载,当前节点到上一个节点
		Self operator--()
		{
			_node = _node->_prev;
			return *this;
		};
		//后置--重载,当前节点到上一个节点
		Self& operator--(int) {
			Node* tmp = _node;
			_node = _node->_prev;
			return tmp;
		};
		//解引用重载
		str operator*()
		{
			return _node->_val;
		};
		//->重载
		ptr operator->()
		{
			return &*this;
		};
		//!=重载
		bool operator!=(const Self& l) {
			return l._node !=_node;
		};
		//==重载
		bool operator==(const Self& l) {
			return l._node == _node;
		};
	};

	//反向迭代器
	template<class T, class str, class ptr>
	struct Reverse_list_iterator
	{
		typedef ListNode<T> Node;
		typedef Reverse_list_iterator<T, str, ptr> Self;
		Node* _node;

		//构造函数
		Reverse_list_iterator(Node* tmp):
			_node(tmp) {};

		//前置++重载
		Self operator++()
		{
			_node = _node->_prev;
			return *this;
		};
		//后置++重载
		Self operator++(int) {
			Node* tmp = _node;
			_node = _node->_prev;
			return tmp;
		};
		//前置--重载,
		Self operator--()
		{
			_node = _node->_next;
			return *this;
		};
		//后置--重载
		Self& operator--(int) {
			Node* tmp = _node;
			_node = _node->_next;
			return tmp;
		};
		//解引用重载
		str operator*()
		{
			return (_node->_prev)->_val;
		};
		//->重载
		ptr operator->()
		{
			return &*this;
		};
		//!=重载
		bool operator!=(const Self& l) {
			return l._node != _node;
		};
		//==重载
		bool operator==(const Self& l) {
			return l._node == _node;
		};

	};

	//list模板
	template<class T>
	class List
	{
	public:
		typedef ListNode<T> Node;
		//迭代器命名
		typedef Listiterator<T, T&, T*> iterator;
		typedef Listiterator<T, const T&, const T*> const_iterator;
		typedef Reverse_list_iterator<T, T&, T*> reverse_list_iterator;
		typedef Reverse_list_iterator<T, const  T&, const  T*> const_reverse_list_iterator;
		//1.  List的构造
		//默认构造
		List()
		{
			createhead();
		}
		//传值构造
		List(int n, const T& value = T())
		{
			createhead();
			for (int i = 0; i < n; ++i)
			push_back(value);
		}
		//迭代器构造
		template <class Iterator>
		List(Iterator first, Iterator last)
		{
			createhead();
			auto start = first;
			while (start != last)
			{
				push_back(*start);
				start++;
			};
		};
		//拷贝构造
		List(const List<T>& l)
		{
			createhead();
			for (auto e : l)
			{
				push_back(e);
			}
		};

		//赋值重载
		List<T>& operator=(List<T> l)
		{
			swap(_pHead, l._pHead);
			return *this;
		}
		//析构函数
		~List()
		{
			clear();
			delete _pHead;
		}


		//2. 迭代器
		iterator begin() {
			return iterator(_pHead->_next);
		};
		iterator end()
		{
			return iterator(_pHead);
		};
		const_iterator begin() const
		{
			return const_iterator(_pHead->_next);
		};
		const_iterator end() const
		{
			return const_iterator(_pHead);
		};

		reverse_list_iterator rbegin(){
			return reverse_list_iterator(_pHead);
		};
		reverse_list_iterator rend()
		{
			return reverse_list_iterator(_pHead->_next);
		};
		const_reverse_list_iterator rbegin() const
		{
			return const_reverse_list_iterator(_pHead);
		};
		const_reverse_list_iterator rend() const
		{
			return const_reverse_list_iterator(_pHead->_next);
		};

		//3. 内容相关
		size_t size()const
		{
			size_t size = 0;
			auto it = begin();
			while (it != end())
			{
				size++;
				it++;
			}
			return size;
		}
		bool empty()const
		{
			return size() == 0;
		}


		//4. 获取头尾元素
		T& front()
		{
			return (_pHead->_next)->_val;
		}
		const T& front()const
		{
			return (_pHead->_next)->_val;
		}
		T& back()
		{
			return (_pHead->_prev)->_val;
		}
		const T& back()const
		{
			return (_pHead->_prev)->_val;
		}

		//5. 内存修改
		void push_back(const T& val) {
			insert(end(), val);
		};
		void pop_back() {
			erase(--end());
		};
		void push_front(const T& val) {
			insert(begin(), val);
		};
		void pop_front() {
			erase(begin());
		};

		// 在pos位置前插入值为val的节点
		//不存在迭代器失效
		iterator insert(iterator pos, const T& val) {
			Node* tmp = new Node(val);
			Node* cur = pos._node;
			Node* prev = cur->_prev;

			tmp->_next = cur;
			tmp->_prev = prev;

			cur->_prev = tmp;
			prev->_next = tmp;

			return  iterator(tmp);
		};

		// 删除pos位置的节点,返回该节点的下一个位置
		//存在迭代器失效
		iterator erase(iterator pos)
		{
			Node* Prew = pos._node->_prev;
			Node* Next = pos._node->_next;
			delete pos._node;
			Prew->_next = Next;
			Next->_prev = Prew;
			return Next;
		};

		void clear() { 
			iterator start = begin();
			while (start != end())
			{
				start = erase(start);
			}
			_pHead->_next = _pHead;
			_pHead->_prev = _pHead;
		}
		void swap(List<T>& l)
		{
			Node tmp = _pHead;
			_pHead = l._pHead;
			l._pHead = tmp;
		}
	private: 
		Node* _pHead;
		void createhead()
		{
			_pHead = new Node;
			_pHead->_prev = _pHead;
			_pHead->_next = _pHead;
		}
	};
}

本文就到这里,感谢你看到这里❤️❤️!
我知道一些人看文章喜欢静静看,不评论🤔,但是他会点赞😍,这样的人,帅气低调有内涵😎,美丽大方很优雅😊,明人不说暗话,要你手上的一个点赞😘!

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

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

相关文章

更换固件后飞控OSD叠显不对/叠显不显示/叠显乱码问题

笔者用的飞控型号为SpeedyBeeF405V4的飞控&#xff0c;OSD叠显芯片型号为AT7456E。 我的这款飞控是支持两款固件的&#xff0c;INAV和BetaFlight。 开始飞控的默认固件为BetaFlight&#xff0c;切换INAV固件后&#xff0c;进行OSD调整&#xff0c;但发现水平线无法正常显示&…

分享一个 ASP.NET Web Api 上传和读取 Excel的方案

前言 许多业务场景下需要处理和分析大量的数据&#xff0c;而 Excel 是业务人员常用的数据表格工具&#xff0c;因此&#xff0c;将 Excel 表格中内容上传并读取到网站&#xff0c;是一个很常见的功能&#xff0c;目前有许多成熟的开源或者商业的第三方库&#xff0c;比如 NPO…

ROS2在RVIZ2中加载机器人urdf模型

参考ROS2-rviz2显示模型 我这边用的solid works生成的urdf以及meshes&#xff0c;比参考的方法多了meshes 问题一&#xff1a;Error retrieving file [package://rm_dcr_description/meshes/leftarm_link7.STL]: Package [rm_dcr_description] does not exist 这个是urdf模型中…

蓝桥杯单片机第五届国赛题目

前言&#xff1a;针对串口的练手&#xff0c;此处只作代码记录&#xff0c;不进行分析和展示 目录 题目代码底层驱动主程序核心代码 题目 代码 注&#xff1a;EEPROM的五组后丢弃用一个记录次数的变量进行循环即可&#xff0c;我没有写这一部分代码。 底层驱动 IIC unsign…

【Linux】Linux环境基础开发工具_3

文章目录 四、Linux环境基础开发工具2. vim3. gcc和g动静态库的理解 未完待续 四、Linux环境基础开发工具 2. vim vim 怎么批量化注释呢&#xff1f;最简单的方法就是在注释开头和结尾输入 /* 或 */ 。当然也可以使用快捷键&#xff1a; Ctrl v 按 hjkl 光标移动进行区域选择…

用HAL库改写江科大的stm32入门-6-3 PWM驱动LED呼吸灯

接线图&#xff1a; 2 :实验目的&#xff1a; 利用pwm实现呼吸灯。 关键PWM定时器设置&#xff1a; 代码部分&#xff1a; int main(void) {/* USER CODE BEGIN 1 *//* USER CODE END 1 *//* MCU Configuration--------------------------------------------------------*…

JVM 常见配置参数

JVM 配置常见参数 Java虚拟机的参数&#xff0c;在启动jar包的时候通过java 命令指定JVM参数 -options表示Java虚拟机的启动参数&#xff0c;class为带有main()函数的Java类&#xff0c;args表示传递给主函数main()的参数。 一、系统查看参数: -XX:PrintVMOptions可以在程序…

ADC数模转换器

一、ADC&#xff08;Analog-Digital Converter&#xff09;模拟-数字转换器 1、ADC可以将引脚上连续变化的模拟电压转换为内存中存储的数字变量&#xff0c;建立模拟电路到数字电路的桥梁 2、12位逐次逼近型ADC&#xff0c;1us转换时间 3、输入电压范围&#xff1a;0~3.3V&a…

Visual Studio 2022创建dll并调用

需求&#xff1a; 创建A项目&#xff0c;有函数和类&#xff0c;将A项目生成DLL动态链接库 创建B项目&#xff0c;使用A项目生成的dll和lib相关文件 正常项目开发.h用于函数声明&#xff0c;.cpp用于函数实现&#xff0c;但是项目开发往往不喜欢将.cpp函数实现的代码发给别人&…

只出现一次的数字II ---- 位运算

题目链接 题目: 分析: 对于只出现一次的数字, 他的任意一个bit位, 可能是0或1对于其余出现3次的数字, 假设有3n个数, 那么他们的任意一个bit相加的和可能是3n个0或3n个1那么对于数组中的全部数字的任意一个bit位之和共有三种情况: 3n个1 1 3n13n个0 1 13n个1 0 3n3n个0…

反VC情绪:加密市场需要新的分布式代币发行方式

GME事件 GME事件反应了社交媒体在金融决策中的影响力&#xff0c;散户投资者群体通过集体行动&#xff0c;改变了很多人对股市的看法和参与方式。 GME事件中&#xff0c;meme扮演了核心角色。散户投资者使用各种meme来沟通策略、激励持股行为&#xff0c;创造了一种反对华尔街…

2.5Bump Mapping 凹凸映射

一、Bump Mapping 介绍 我们想要在屏幕上绘制物体的细节&#xff0c;从尺度上讲&#xff0c;一个物体的细节分为&#xff1a;宏观、中观、微观宏观尺度中其特征会覆盖多个像素&#xff0c;中观尺度只覆盖几个像素&#xff0c;微观尺度的特征就会小于一个像素宏观尺度是由顶点或…

为何懂行的人都在选海信Mini LED?

今年的618大促比往年来得要更早一些。纵览各电商平台的电视产品&#xff0c;能发现Mini LED电视的出镜率很高&#xff0c;成了各大品牌的主推产品。 对于什么样的Mini LED更值得买&#xff0c;各品牌都有自己的说辞。因为缺乏科学系统的选购标准&#xff0c;消费者容易在各方说…

【VSCode】快捷方式log去掉分号

文章目录 一、引入二、解决办法 一、引入 我们使用 log 快速生成的 console.log() 都是带分号的 但是我们的编程习惯都是不带分号&#xff0c;每次自动生成后还需要手动删掉分号&#xff0c;太麻烦了&#xff01; 那有没有办法能够生成的时候就不带分号呢&#xff1f;自然是有…

国产身份域控在接管计算机登录时,要考虑哪些场景?

当客户提出需要宁盾来接管计算机做统一认证时&#xff0c;我们知道&#xff0c;他要的其实有辣么多&#xff1a; 一、多操作系统的统一登录 信创项目中&#xff0c;客户的计算机操作系统存在windows、Linux以及麒麟、统信等国产操作系统混合使用的情况&#xff0c;而员工想要的…

云动态摘要 2024-05-31

给您带来云厂商的最新动态&#xff0c;最新产品资讯和最新优惠更新。 最新优惠与活动 [1.5折起]年中盛惠--AI分会场 腾讯云 2024-05-30 人脸核身、语音识别、文字识别、数智人、腾讯混元等热门AI产品特惠&#xff0c;1.5折起 云服务器ECS试用产品续用 阿里云 2024-04-14 云…

Zynq学习笔记--AXI4-Stream 图像数据从仿真输出到图像文件

目录 1. 简介 2. 构建工程 2.1 Vivado 工程 2.2 TestBench 代码 2.3 关键代码分析 3. VPG Background Pattern ID (0x0020) Register 4. 总结 1. 简介 使用 SystemVerilog 将 AXI4-Stream 图像数据从仿真输出到图像文件 (PPM)。 用到的函数包括 $fopen、$fwrite 和 $f…

dmdts连接kingbase8报错

dmdts连接kingbase报错 环境介绍1 人大金仓jdbc配置2 dmdts 人大金仓jdbc默认配置3 dmdts 修改jdbc配置4 达梦产品学习使用列表 环境介绍 dts版本 使用dmdts连接kingbase金仓数据库报错 无效的URL 对比jdbc连接串,修改配置解决 1 人大金仓jdbc配置 配置URL模版信息等 类名…

LabVIEW中PID控制器系统的噪声与扰动抑制策略

在LabVIEW中处理PID控制器系统中的噪声和外部扰动&#xff0c;需要从信号处理、控制算法优化、硬件滤波和系统设计四个角度入手。采用滤波技术、调节PID参数、增加前馈控制和实施硬件滤波器等方法&#xff0c;可以有效减少噪声和扰动对系统性能的影响&#xff0c;提高控制系统的…

2020 6.s081——Lab2:system calls

左岸的一座白色环形阶梯 浪人正在用和弦练习忧郁 晨曦下的少女听着吉他旋律 在许愿池边巴洛克式的叹息 ——许愿池的希腊少女 完整代码见&#xff1a;SnowLegend-star/6.s081 at syscall (github.com) System call tracing (moderate) 这个实验要求我们跟踪系统调用。 感觉实…