C++:关于模拟实现vector和list中迭代器模块的理解

news2024/9/25 21:23:23

文章目录

  • list和vector的迭代器对比
  • list的实现过程
  • 完整代码

本篇是关于vectorlist的模拟实现中,关于迭代器模块的更进一步理解,以及在前文的基础上增加对于反向迭代器的实现和库函数的对比等

本篇是写于前面模拟实现的一段时间后,重新回头看迭代器的实现,尤其是在模板角度对list中迭代器封装的部分进行解析,希望可以对迭代器的封装实现有更深层次的理解,同时将与库内的实现做对比进行优化处理

list和vector的迭代器对比

listvector的迭代器实现是不一样的,在vector中的迭代器就是一个原生指针,因此在实现的时候也就是用的原生指针即可,但是对于list来说不能这样,list的迭代器中例如++--这样的操作,是不能和vector中的原生指针一样直接去实现的,而是需要用next或者是prior来模拟这个过程,还有例如*和->这样的访问数据的模式,因此在list的迭代器实现中是使用了封装来进行实现的

STL中有六大组件,其中迭代器算其中一个,那么也就意味着迭代器具有通用的功能,例如下面的代码,在使用构造函数时可以使用迭代器进行构造,而这个构造的过程使用的迭代器对于各种容器来说都是通用的:

#include <iostream>
#include <vector>
#include <list>
using namespace std;

int main()
{
	vector<int> v1{ 1,2,3,4,5,3,2,2 };
	vector<int> v2(v1.begin(), v1.end());
	list<int> l1(v1.begin(), v1.end());
	return 0;
}

那么就意味着,在实现迭代器的过程中也是就可以根据迭代器来进行不同容器间的构造

list的实现过程

首先,在list的实现过程中要有下面几个部分组成:list中包含的节点,list的迭代器访问,list的节点之间的关系

那么首先就是list中节点的定义,用到了模板,现在再对该部分进行解析,其实就是在创建的时候可以根据需要的内容开辟出对应的节点类型

template<class T>
struct list_node
{
	T _data;
	list_node<T>* _next;
	list_node<T>* _prev;

	list_node(const T& x = T())
		:_data(x)
		, _next(nullptr)
		, _prev(nullptr)
	{}
};

有了节点信息,就可以对list做一些基础的操作,例如头插头删,尾插尾删等操作,但对于inserterase这些操作还不能够实现,因此就要实现迭代器模块的内容

不管是对于vector还是list,迭代器的本质就是用一个原生指针去进行访问等等,但是listvector不同的是,list的存储地址并不是连续的,因此在访问的过程中就需要对迭代器进行一定程度的封装,例如在++或者--的操作符上可以进行一些体现,因此list迭代器的实现是需要进行单独的封装的

// 定义正向迭代器
template <class T, class Ref, class Ptr>
class __list_iterator
{
public:
	typedef list_node<T> Node;
	typedef __list_iterator<T, T&, T*> self;
	__list_iterator(Node* node)
		:_node(node)
	{}

	self& operator++()
	{
		_node = _node->_next;
		return *this;
	}
	self& operator++(int)
	{
		self tmp(*this);
		_node = _node->_next;
		return tmp;
	}
	self& operator--()
	{
		_node = _node->_prev;
		return *this;
	}
	self& operator--(int)
	{
		self tmp(*this);
		_node = _node->_prev;
		return tmp;
	}
	Ref operator*()
	{
		return _node->_data;
	}
	Ptr operator->()
	{
		return &_node->_data;
	}
	bool operator !=(const self& s)
	{
		return _node != s._node;
	}
	bool operator ==(const self& s)
	{
		return _node == s._node;
	}
	Node* _node;
};

上面的片段对list进行了一些封装,实现了它的一些基础功能,从代码中也能看出来,迭代器的本质确确实实就是指针,用指针来进行一系列的操作,对下面这几个片段单独进行解析:

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

这是对于迭代器中解引用的运算符重载,这里使用的是&_node->_data的写法,看似很怪,但是实际上这里的函数调用过程应该是这样

在这里插入图片描述
也就是说,这里对于->进行运算符重载后,还需要进行解引用,这个运算符重载函数返回的是一个指针,而这个指针还要继续进行解引用才能得到用户想要得到的值,编译器在这里进行了特殊处理,两个->使得可读性下降,因此将两个->进行了一定程度的合并,只需要一个->就可以实现解引用的目的

其次是对模板的使用部分

template <class T, class Ref, class Ptr>

	typedef list_node<T> Node;
	typedef __list_iterator<T, T&, T*> self;

这里在最开始定义的时候,就定义了数据类型,引用和指针三种模板参数,借助这个参数就可以用模板将复杂的内容实现简单化,只需要一份代码,模板就可以直接实例化出用户在调用的时候需要的代码,在进行封装const迭代器的时候,只需要提供的参数改为const版本就可以实现目的,很便捷

这样就实现了正向迭代器的普通版本,而对于const迭代器的版本也只需要进行不同的模板参数就可以实例化出const版本的迭代器

typedef __list_iterator<T, T&, T*> iterator;
typedef __list_iterator<T, const T&, const T*> const_iterator;

有了迭代器,在实现链表的各种函数功能就更加方便,例如可以实现eraseinsert的操作,实现了这两个函数,在进行头插头删,尾插尾删的时候就更加方便,只需要进行原地的调用即可

	// Modifiers
	void push_front(const T& val)
	{
		//Node* newnode = new Node;
		//newnode->_data = val;
		//newnode->_next = _head->_next;
		//_head->_next->_prev = newnode;
		//_head->_next = newnode;
		//newnode->_prev = _head;
		//_size++;
		insert(begin(), val);
	}
	void pop_front()
	{
		//Node* delnode = _head->_next;
		//_head->_next = _head->_next->_next;
		//_head->_next->_prev = _head;
		//delete delnode;
		//delnode = nullptr;
		//_size--;
		erase(begin());
	}
	void push_back(const T& val)
	{
		//Node* newnode = new Node;
		//newnode->_data = val;
		//newnode->_prev = _head->_prev;
		//_head->_prev->_next = newnode;
		//newnode->_next = _head;
		//_head->_prev = newnode;
		//_size++;
		insert(end(), val);
	}
	void pop_back()
	{
		//Node* delnode = _head->_prev;
		//delnode->_prev->_next = _head;
		//_head->_prev = delnode->_prev;
		//delete delnode;
		//delnode = nullptr;
		//_size--;
		erase(--end());
	}
	iterator insert(iterator pos, const T& val)
	{
		Node* cur = pos._node;
		Node* prev = cur->_prev;
		Node* newnode = new Node(val);

		_size++;
		prev->_next = newnode;
		newnode->_prev = prev;
		newnode->_next = cur;
		cur->_prev = newnode;
		return iterator(newnode);
	}
	iterator erase(iterator pos)
	{
		Node* cur = pos._node;
		Node* prev = cur->_prev;
		Node* next = cur->_next;

		delete cur;
		cur = nullptr;
		_size--;

		prev->_next = next;
		next->_prev = prev;
		return iterator(next);
	}

那么上面是对前面已经有过的内容进行的重新温故,但是上面的代码其实是没有实现反向迭代器的,而在STL的库中,反向迭代器的定义和正向迭代器其实并不相同,它是直接借助正向迭代器对反向迭代器进行适配,有些类似于用vectorlist或者deque对栈和队列进行的适配,也体现了封装和代码复用

template<class Iterator, class Ref, class Ptr>
class ReverseIterator
{
public:
	typedef ReverseIterator<Iterator, Ref, Ptr> Self;

	ReverseIterator(Iterator it)
		:_it(it)
	{}

	Self& operator++()
	{
		--_it;
		return *this;
	}

	Ref operator*()
	{
		return *_it;
	}

	Ptr operator->()
	{
		return _it.operator->();
	}

	bool operator!=(const Self& s)
	{
		return _it != s._it;
	}
private:
	Iterator _it;
};

上面的代码就是对于反向迭代器的封装,从中可以看出反向迭代器是使用了正向迭代器的,并且在它的基础上进行的修改,因此这个反向迭代器是可以适配于vector也可以适配于list的

整体来说,对于反向迭代器就是用正向迭代器进行适配出来的,体现了模板的好处

完整代码

#pragma once

namespace mylist
{
	// 定义节点内容
	template <class T>
	struct list_node
	{
		list_node(const T& x = T())
			:_data(x)
			, _next(nullptr)
			, _prev(nullptr)
		{}
		T _data;
		list_node<T>* _next;
		list_node<T>* _prev;
	};

	// 定义正向迭代器
	template <class T, class Ref, class Ptr>
	class __list_iterator
	{
	public:
		typedef list_node<T> Node;
		typedef __list_iterator<T, T&, T*> self;
		__list_iterator(Node* node)
			:_node(node)
		{}

		self& operator++()
		{
			_node = _node->_next;
			return *this;
		}
		self& operator++(int)
		{
			self tmp(*this);
			_node = _node->_next;
			return tmp;
		}
		self& operator--()
		{
			_node = _node->_prev;
			return *this;
		}
		self& operator--(int)
		{
			self tmp(*this);
			_node = _node->_prev;
			return tmp;
		}
		Ref operator*()
		{
			return _node->_data;
		}
		Ptr operator->()
		{
			return &_node->_data;
		}
		bool operator !=(const self& s)
		{
			return _node != s._node;
		}
		bool operator ==(const self& s)
		{
			return _node == s._node;
		}
		Node* _node;
	};

	// 定义反向迭代器
	template<class Iterator, class Ref, class Ptr>
	class reverse_iterator
	{
		typedef reverse_iterator<Iterator, Ref, Ptr> self;
	public:
		reverse_iterator(Iterator it)
			:_it(it)
		{}

		self& operator++()
		{
			_it--;
			return *this;
		}
		self& operator++(int)
		{
			self tmp(*this);
			_it--;
			return tmp;
		}
		self& operator--()
		{
			_it++;
			return *this;
		}
		self& operator--(int)
		{
			self tmp(*this);
			_it++;
			return tmp;
		}
		Ref operator*()
		{
			Iterator cur = _it;
			return *(--cur);
		}
		Ptr operator->()
		{
			return &(operator*());
		}
		bool operator !=(const self& s)
		{
			return _it != s._it;
		}
		bool operator ==(const self& s)
		{
			return _it == s._it;
		}

		Iterator _it;
	};

	// 定义链表
	template <class T>
	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;
		typedef reverse_iterator<iterator, T&, T*> reverse_iterator;
		//typedef reverse_iterator<const_iterator, const T&, const T*> const_reverse_iterator;
		// Constructor
		list()
		{
			empty_init();
		}
		list(const list<T>& lt)
		{
			for (auto x : lt)
			{
				push_back(x);
			}
		}
		list(iterator begin, iterator end)
		{
			empty_init();
			while (begin != end)
			{
				push_back(begin._node->_data);
				begin++;
			}
		}
		//list(reverse_iterator rbegin, reverse_iterator rend)
		//{
		//	empty_init();
		//	while (rbegin != rend)
		//	{
		//		push_back(rbegin._node->_data);
		//		rbegin++;
		//	}
		//}

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

		// Operator=
		list<T>& operator=(const list<T>& lt)
		{
			swap(lt);
			return *this;
		}

		// Iterators
		iterator begin()
		{
			return iterator(_head->_next);
		}
		iterator end()
		{
			return iterator(_head);
		}
		const_iterator begin() const
		{
			return const_iterator(_head->_next);
		}
		const_iterator end() const
		{
			return const_iterator(_head);
		}
		reverse_iterator rbegin()
		{
			return reverse_iterator(end());
		}
		reverse_iterator rend()
		{
			return reverse_iterator(begin());
		}
		//const_reverse_iterator rbegin() const
		//{
		//	return const_reverse_iterator(end());
		//}
		//const_reverse_iterator rend() const
		//{
		//	return const_reverse_iterator(begin());
		//}

		// Capacity
		bool empty()
		{
			return _size == 0;
		}
		size_t size()
		{
			return _size;
		}

		// Element access
		T& front()
		{
			return begin()._node->_data;
		}
		T& back()
		{
			return (--end())._node->_data;
		}

		// Modifiers
		void push_front(const T& val)
		{
			//Node* newnode = new Node;
			//newnode->_data = val;
			//newnode->_next = _head->_next;
			//_head->_next->_prev = newnode;
			//_head->_next = newnode;
			//newnode->_prev = _head;
			//_size++;
			insert(begin(), val);
		}
		void pop_front()
		{
			//Node* delnode = _head->_next;
			//_head->_next = _head->_next->_next;
			//_head->_next->_prev = _head;
			//delete delnode;
			//delnode = nullptr;
			//_size--;
			erase(begin());
		}
		void push_back(const T& val)
		{
			//Node* newnode = new Node;
			//newnode->_data = val;
			//newnode->_prev = _head->_prev;
			//_head->_prev->_next = newnode;
			//newnode->_next = _head;
			//_head->_prev = newnode;
			//_size++;
			insert(end(), val);
		}
		void pop_back()
		{
			//Node* delnode = _head->_prev;
			//delnode->_prev->_next = _head;
			//_head->_prev = delnode->_prev;
			//delete delnode;
			//delnode = nullptr;
			//_size--;
			erase(--end());
		}
		iterator insert(iterator pos, const T& val)
		{
			Node* cur = pos._node;
			Node* prev = cur->_prev;
			Node* newnode = new Node(val);

			_size++;
			prev->_next = newnode;
			newnode->_prev = prev;
			newnode->_next = cur;
			cur->_prev = newnode;
			return iterator(newnode);
		}
		iterator erase(iterator pos)
		{
			Node* cur = pos._node;
			Node* prev = cur->_prev;
			Node* next = cur->_next;

			delete cur;
			cur = nullptr;
			_size--;

			prev->_next = next;
			next->_prev = prev;
			return iterator(next);
		}
		void swap(const list<T>& lt)
		{
			std::swap(_head, lt._head);
			std::swap(_size, lt._size);
		}
		void clear()
		{
			Node* cur = _head->_next;
			while (cur != _head)
			{
				Node* next = cur->_next;
				delete(cur);
				cur = next;
			}
			_head->_next = _head;
			_head->_prev = _head;
		}


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

		void Print()
		{
			Node* cur = _head->_next;
			while (cur != _head)
			{
				cout << cur->_data << "->";
				cur = cur->_next;
			}
			cout << "null" << endl;
		}
	private:
		Node* _head;
		size_t _size;
	};

	void test_iterator()
	{
		list<int> lt;
		lt.push_back(1);
		lt.push_back(2);
		lt.push_back(3);
		lt.push_back(4);
		cout << "iterator constructor:";
		list<int> lt1(lt.begin(), lt.end());
		lt1.Print();
		cout << "front():" << lt.front() << endl;
		cout << "back():" << lt.back() << endl;
		mylist::__list_iterator<int, int&, int*> it = lt.begin();
		while (it != lt.end())
		{
			std::cout << *it << " ";
			it++;
		}
		cout << endl;
	}
	void test_clear()
	{
		list<int> lt1;
		cout << "push_back:" << endl;
		lt1.push_back(1);
		lt1.push_back(2);
		lt1.push_back(3);
		lt1.push_back(4);
		lt1.Print();
		lt1.clear();
		lt1.push_back(5);
		lt1.push_back(6);
		lt1.push_back(7);
		lt1.push_back(8);
		lt1.Print();
	}
	void test_push_pop()
	{
		list<int> lt1;
		cout << "push_back:" << endl;
		lt1.push_back(1);
		lt1.push_back(2);
		lt1.push_back(3);
		lt1.push_back(4);
		lt1.Print();
		cout << "pop_back:" << endl;
		lt1.pop_back();
		lt1.Print();

		list<int> lt2;
		cout << "push_front:" << endl;
		lt2.push_front(1);
		lt2.push_front(2);
		lt2.push_front(3);
		lt2.push_front(4);
		lt2.Print();
		cout << "pop_front:" << endl;
		lt2.pop_front();
		lt2.Print();
	}
	void test_reverse_iterator()
	{
		list<int> lt;
		lt.push_back(1);
		lt.push_back(2);
		lt.push_back(3);
		lt.push_back(4);
		cout << "iterator constructor:";
		//list<int> lt1(lt.rbegin(), lt.rend());
		//lt1.Print();
		cout << "front():" << lt.front() << endl;
		cout << "back():" << lt.back() << endl;
		mylist::list<int>::reverse_iterator rit = lt.rbegin();
		while (rit != lt.rend())
		{
			std::cout << *rit << " ";
			rit++;
		}
		cout << endl;
	}

	void test()
	{
		test_reverse_iterator();
		//test_push_pop();
		//test_clear();
	}
}

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

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

相关文章

逍遥魔兽:如何在服务器上挂机器人?

逍遥魔兽是一款备受欢迎的魔兽世界经典版本&#xff0c;对于许多玩家来说&#xff0c;为了提升游戏体验和效率&#xff0c;他们希望能够在服务器上挂机器人。本文将为您详细讲解如何实现在逍遥魔兽服务器上挂机器人&#xff0c;以提高游戏进程的自动化效率。 第一部分&#x…

正则表达式(Regular Expression)学习网址分享

正则表达式&#xff08;Regular expressions&#xff0c;也叫REs、 regexs 或regex patterns&#xff09;&#xff0c;是一种文本模式&#xff0c;包括普通字符&#xff08;例如&#xff0c;a 到z 之间的字母&#xff09;和特殊字符&#xff08;称为"元字符"&#xf…

数据库系统工程师------流水线

流水线 流水线周期&#xff1a;工序中最长的那段执行时间。 流水线计算公式&#xff1a;第一条指令计算时间 &#xff08;指令条数 - 1&#xff09;*流水线周期。 流水线吞吐率&#xff1a;指单位时间内流水线完成的任务数量或输出的结果数量。 流水线的加速比&#xff1a;完…

CentOS 编译安装Redis

一、编译配置hiredis.h C来操作redis数据库。通过hiredis接口来实现&#xff0c;目前只能在Linux环境使用。 下载hiredis.h hiredis的下载地址为&#xff1a;https://github.com/redis/hiredis 解压并编译hiredis [rootlocalhost source_code]# pwd /usr/local/source_…

Django实战项目-学习任务系统-用户注册

接着上期代码框架&#xff0c;开发第2个功能&#xff0c;用户注册&#xff0c;在原有用户模型基础上&#xff0c;增加一个学生用户属性表&#xff0c;用来关联学生用户的各种属性值&#xff0c;这个属性表是参考网络小说里系统属性值设计的&#xff0c;方便直观了解用户的能力高…

华为OD机试 - 组成最大数(Java 2023 B卷 100分)

目录 专栏导读一、题目描述二、输入描述三、输出描述四、解题思路五、Java算法源码六、效果展示1、输入2、输出 华为OD机试 2023B卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试&#xff08;JAVA&#xff09;真题&#xff08;A卷B卷&#xff09;》…

香港专用服务器拥有良好的国际网络连接

香港服务器在多个领域有着广泛的应用。无论是电子商务、金融交易、游戏娱乐还是社交媒体等&#xff0c;香港服务器都能够提供高效稳定的服务。对于跨境电商来说&#xff0c;搭建香港服务器可以更好地满足亚洲用户的购物需求&#xff1b;对于金融机构来说&#xff0c;香港服务器…

安科瑞ARB5系列弧光保护装置,智能电弧光保护,保障用电安全

安科瑞虞佳豪壹捌柒陆壹伍玖玖零玖叁 什么是弧光 电弧是放电过程中发生的一种现象&#xff0c;当两点之间的电压超过其工频绝缘强度极限时就会发生。当适当的条件出现时&#xff0c;一个携带着电流的等离子产生&#xff0c;直到电源侧的保护设备断开才会消失。空气在通常条件…

31 数据分析(中)numpy介绍

文章目录 工具excelTableauPower Queryjupytermatplotlibnumpy安装导入包快速掌握&#xff08;bushi&#xff09;array和list的相互转化 np的range多维数组的属性array的改变形状array升降维度array内元素的类型数和array的运算array之间的加减法认识轴切片条件与逻辑修改值app…

大规模语言模型高效调参--混合高效微调系列(MAM Adapter,UniPELT)

近年来提出了多种参数高效的迁移学习方法&#xff0c; 这些方法仅微调少量(额外) 参数即可获得强大的性能。虽 然有效&#xff0c; 但人们对为什么有效的关键要素以及各种高效微调方法之间的联系知之甚少。 Adapter 、Prefix Tuning、 LoRA (在结构上和公式上)看起来都不太一样…

SpringCloud小项目——订单积分商城 使用Nacos、Open Feign、Gateway、Sentinel技术栈

目录 引出小项目要求创建极简数据库表订单表&#xff0c;订单明细表商品表积分表 相关微服务积分微服务产品微服务订单微服务调用积分和订单微服务 网关微服务登陆认证通过网关实现对外提供接口API走网关功能 sentinel相关使用Sentinel限流&#xff0c;流量整形Sentinel降级服务…

SparseBEV:High-Performance Sparse 3D Object Detection from Multi-Camera Videos

参考代码&#xff1a;SparseBEV 动机与主要贡献&#xff1a; BEV感知可以按照是否显式构建BEV特征进行划分&#xff0c;显式构建BEV特征的方法需要额外计算量用于特征构建&#xff0c;而类似query方案的方法则不需要这样做。比较两种类型的方法&#xff0c;前者需要更多计算资…

微服务 BFF 架构设计

在现代软件开发中&#xff0c;由于程序、团队、数据规模太大&#xff0c;需要把企业的业务能力进行复用&#xff0c;将领域服务剥离&#xff0c;提供通用能力&#xff0c;避免重复建设和代码&#xff1b;另外服务功能的弹性能力不一样&#xff0c;比如定时任务、数据同步明确的…

mysql面试题39:什么是触发器?触发器的使用场景有哪些?MySQL中都有哪些触发器?

该文章专注于面试,面试只要回答关键点即可,不需要对框架有非常深入的回答,如果你想应付面试,是足够了,抓住关键点 面试官:什么是触发器?触发器的使用场景有哪些? 触发器(Trigger)是数据库中一种特殊类型的存储过程,它在特定的数据库事件(例如插入、更新、删除操作…

软考高级架构师下篇-18大数据架构理论设计与实践

目录 1. 引言2. 传统数据处理系统的问题1.传统数据库的数据过载问题2.大数据的特点3.大数据利用过程4.大数据处理系统架构分析3.典型的大数据架构1. Lambda架构2.Kappa架构3. Lambda架构与Kappa架构的对比4.大数据架构的实践1.大规模视频网络2.广告平台3.公司智能决策大数据系统…

Redis-集群

Redis-集群 主从复制和哨兵只能在主节点进行写数据&#xff0c;从节点读取数据&#xff0c;因此本质上&#xff0c;是进行了读写的分离&#xff0c;每个节点都保存了所有的数据&#xff0c;并不能实现一个很好的分布式效果。 1.哈希求余算法 假设有N台主机&#xff0c;对每台…

对比纯软开与嵌入式硬件开发谁更好呢?

对比纯软开与嵌入式硬件开发谁更好呢&#xff1f; 你的纠结和犹豫是理解的&#xff0c;职业选择确实是一个重要的决策。我明白你在嵌入式和软件开发之间犹豫不决的原因。让我给你提供一些建议&#xff0c;帮助你做出更明智的决定。最近很多小伙伴找我&#xff0c;说想要一些嵌入…

日期时间参数,格式配置(SpringBoot)

介绍 在SpringBoot项目中&#xff0c;接口中的日期和时间类型的参数&#xff0c;配置格式。 日期格式 接口中常用的日期时间格式有两种&#xff1a; 字符串&#xff08;比如&#xff1a;yyyy-MM-dd HH:mm:ss&#xff09;时间戳&#xff08;比如&#xff1a;1696839876955&a…

【云计算网络安全】僵尸网络详解:工作原理、控制和保护方法

文章目录 一、什么是僵尸网络&#xff1f;二、僵尸网络因为什么原因而诞生&#xff1f;三、僵尸网络主要用途四、僵尸网络如何工作&#xff1f;五、如何控制僵尸网络&#xff1f;5.1 客户端/服务器僵尸网络模型5.1.1 星形网络拓扑5.1.2 多服务器网络拓扑5.1.3 分层网络拓扑 5.2…

Maven依赖解决

记一次Maven依赖冲突解决 以zookeeper为例 一、问题描述 当下载zookeeper的2.2.6.RELEASE时&#xff0c;报错 Could not find artifact org.springframework.cloud:spring-cloud-starter-zookeeper-discovery:pom:2.2.6.RELEASE in central (https://repo.maven.apache.org/ma…