STL详解— list类的模拟实现

news2024/11/22 16:38:33

本文章所需实现三个类及其每个类里的各个函数接口总览:

namespace zhc
{
	//模拟实现list当中的结点类
	template<class T>
	struct list_node
	{
		//成员函数
		list_node(const T& val = T()); //构造函数

		//成员变量
		T _val;                 //数据域
		list_node<T>* _next;   //后继指针
		list_node<T>* _prev;   //前驱指针
	};

	//模拟实现list迭代器
	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* pnode);  //构造函数

		//各种运算符重载函数
		self operator++();
		self operator--();
		self operator++(int);
		self operator--(int);
		bool operator==(const self& s) const;
		bool operator!=(const self& s) const;
		Ref operator*();
		Ptr operator->();

		//成员变量
		node* _pnode; //一个指向结点的指针
	};

	//模拟实现list
	template<class T>
	class list
	{
	public:
		typedef _list_node<T> node;
		typedef _list_iterator<T, T&, T*> iterator;
		typedef _list_iterator<T, const T&, const T*> const_iterator;

		//默认成员函数
		list();
		list(const list<T>& lt);
		list<T>& operator=(const list<T>& lt);
		~list();

		//迭代器相关函数
		iterator begin();
		iterator end();
		const_iterator begin() const;
		const_iterator end() const;

		//访问容器相关函数
		T& front();
		T& back();
		const T& front() const;
		const T& back() const;

		//插入、删除函数
		void insert(iterator pos, const T& x);
		iterator erase(iterator pos);
		void push_back(const T& x);
		void pop_back();
		void push_front(const T& x);
		void pop_front();

		//其他函数
		size_t size() const;
		void resize(size_t n, const T& val = T());
		void clear();
		bool empty() const;
		void swap(list<T>& lt);

	private:
		node* _head; //指向链表头结点的指针
	};
}

结点类的模拟实现

我们经常说list在底层实现时就是一个链表,更准确来说,list实际上是一个带头双向循环链表。

 

因此,我们若要实现list,则首先需要实现一个结点类。而一个结点需要存储的信息有:数据、前一个结点的地址、后一个结点的地址,于是该结点类的成员变量也就出来了(数据、前驱指针、后继指针)。

而对于该结点类的成员函数来说,我们只需实现一个构造函数即可。因为该结点类只需要根据数据来构造一个结点即可,而结点的释放则由list的析构函数来完成。

构造函数

结点类的构造函数直接根据所给数据构造一个结点即可,构造出来的结点的数据域存储的就是所给数据,而前驱指针和后继指针均初始化为空指针即可。

//构造函数
list_node(const T& val = T())
	:_val(val)
	, _prev(nullptr)
	, _next(nullptr)
{}

注意: 若构造结点时未传入数据,则默认以list容器所存储类型的默认构造函数所构造出来的值为传入数据。

迭代器类的模拟实现

迭代器类存在的意义

之前模拟实现string和vector时都没有说要实现一个迭代器类,为什么实现list的时候就需要实现一个迭代器类了呢?

因为string和vector对象都将其数据存储在了一块连续的内存空间,我们通过指针进行自增、自减以及解引用等操作,就可以对相应位置的数据进行一系列操作,因此string和vector当中的迭代器就是原生指针。

但是对于list来说,其各个结点在内存当中的位置是随机的,并不是连续的,我们不能仅通过结点指针的自增、自减以及解引用等操作对相应结点的数据进行操作。

 

而迭代器的意义就是,让使用者可以不必关心容器的底层实现,可以用简单统一的方式对容器内的数据进行访问。

既然list的结点指针的行为不满足迭代器定义,那么我们可以对这个结点指针进行封装,对结点指针的各种运算符操作进行重载,使得我们可以用和string和vector当中的迭代器一样的方式使用list当中的迭代器。例如,当你使用list当中的迭代器进行自增操作时,实际上执行了p = p->next语句,只是你不知道而已。
总结: list迭代器类,实际上就是对结点指针进行了封装,对其各种运算符进行了重载,使得结点指针的各种行为看起来和普通指针一样。(例如,对结点指针自增就能指向下一个结点)

迭代器类的模板参数说明

 这里我们所实现的迭代器类的模板参数列表当中为什么有三个模板参数?

template<class T, class Ref, class Ptr>

在list的模拟实现当中,我们typedef了两个迭代器类型,普通迭代器和const迭代器。

typedef _list_iterator<T, T&, T*> iterator;  //普通迭代器
typedef _list_iterator<T, const T&, const T*> const_iterator;//const 迭代器

这里我们就可以看出,迭代器类的模板参数列表当中的Ref和Ptr分别代表的是引用类型和指针类型。

当我们使用普通迭代器时,编译器就会实例化出一个普通迭代器对象;当我们使用const迭代器时,编译器就会实例化出一个const迭代器对象。

若该迭代器类不设计三个模板参数,那么就不能很好的区分普通迭代器和const迭代器。

构造函数

迭代器类实际上就是对结点指针进行了封装,其成员变量就只有一个,那就是结点指针,其构造函数直接根据所给结点指针构造一个迭代器对象即可。

//构造函数
_list_iterator(node* pnode)
	:_pnode(pnode)
{}

++运算符的重载

首先是前置++,前置++原本的作用是将数据自增,然后返回自增后的数据。我们的目的是让结点指针的行为看起来更像普通指针,那么对于结点指针的前置++,我们就应该先让结点指针指向后一个结点,然后再返回“自增”后的结点指针即可。

//前置++
self operator++()
{
	_pnode = _pnode->_next; //让结点指针指向后一个结点
	return *this; //返回自增后的结点指针
}

对于后置++,我们则应该先记录当前结点指针的指向,然后让结点指针指向后一个结点,最后返回“自增”前的结点指针即可。

//后置++
self operator++(int)
{
	self tmp(*this); //记录当前结点指针的指向
	_pnode = _pnode->_next; //让结点指针指向后一个结点
	return tmp; //返回自增前的结点指针
}

说明: self是当前迭代器对象的类型:

typedef _list_iterator<T, Ref, Ptr> self;

--运算符的重载

对于前置- -,我们应该先让结点指针指向前一个结点,然后再返回“自减”后的结点指针即可。

//前置--
self operator--()
{
	_pnode = _pnode->_prev; //让结点指针指向前一个结点
	return *this; //返回自减后的结点指针
}

而对于后置- -,我们则应该先记录当前结点指针的指向,然后让结点指针指向前一个结点,最后返回“自减”前的结点指针即可。

//后置--
self operator--(int)
{
	self tmp(*this); //记录当前结点指针的指向
	_pnode = _pnode->_prev; //让结点指针指向前一个结点
	return tmp; //返回自减前的结点指针
}

==运算符的重载

当使用==运算符比较两个迭代器时,我们实际上想知道的是这两个迭代器是否是同一个位置的迭代器,也就是说,我们判断这两个迭代器当中的结点指针的指向是否相同即可。

bool operator==(const self& s) const
{
	return _pnode == s._pnode; //判断两个结点指针指向是否相同
}

!=运算符的重载

!=运算符刚好和==运算符的作用相反,我们判断这两个迭代器当中的结点指针的指向是否不同即可。

bool operator!=(const self& s) const
{
	return _pnode != s._pnode; //判断两个结点指针指向是否不同
}

*运算符的重载

当我们使用解引用操作符时,是想得到该位置的数据内容。因此,我们直接返回当前结点指针所指结点的数据即可,但是这里需要使用引用返回,因为解引用后可能需要对数据进行修改。

Ref operator*()
{
	return _pnode->_val; //返回结点指针所指结点的数据
}

->运算符的重载

某些情景下,我们使用迭代器的时候可能会用到->运算符。
想想如下场景:
 当list容器当中的每个结点存储的不是内置类型,而是自定义类型,例如日期类
,那么当我们拿到一个位置的迭代器时,我们可能会使用->运算符访问Date的成员:

	list<Date> lt;
	Date d1(2023, 5, 24);
	Date d2(2023, 5, 23);
	Date d3(2023, 5, 22);
	lt.push_back(d1);
	lt.push_back(d2);
	lt.push_back(d3);
	list<Date>::iterator pos = lt.begin();
	cout << pos->_year << endl; //输出第一个日期的年份

注意: 使用pos->_year这种访问方式时,需要将日期类的成员变量设置为公有。

对于->运算符的重载,我们直接返回结点当中所存储数据的地址即可。

Ptr operator->()
{
	return &_pnode->_val; //返回结点指针所指结点的数据的地址
}

讲到这里,可能你会觉得不对,按照这种重载方式的话,这里使用迭代器访问日期类当中的成员变量时不是应该用两个->吗?

这里本来是应该有两个->的,第一个箭头是pos ->去调用重载的operator->返回Date* 的指针,第二个箭头是Date* 的指针去访问对象当中的成员变量_year。

但是一个地方出现两个箭头,程序的可读性太差了,所以编译器做了特殊识别处理,为了增加程序的可读性,省略了一个箭头。

list的模拟实现

默认成员函数

构造函数

list是一个带头双向循环链表,在构造一个list对象时,直接申请一个头结点,并让其前驱指针和后继指针都指向自己即可。

//构造函数
list()
{
	_head = new node; //申请一个头结点
	_head->_next = _head; //头结点的后继指针指向自己
	_head->_prev = _head; //头结点的前驱指针指向自己
}

 拷贝构造函数

拷贝构造函数就是根据所给list容器,拷贝构造出一个对象。对于拷贝构造函数,我们先申请一个头结点,并让其前驱指针和后继指针都指向自己,然后将所给容器当中的数据,通过遍历的方式一个个尾插到新构造的容器后面即可。

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

clear

clear函数用于清空容器,我们通过遍历的方式,逐个删除结点,只保留头结点即可。

void clear()
{
	iterator it = begin();
	while (it != end()) //逐个删除结点,只保留头结点
	{
		it = erase(it);
	}
}

赋值运算符
重载函数

对于赋值运算符的重载,这里提供两种写法:
写法一:现代写法
这是比较容易理解的一种写法,先调用clear函数将原容器清空,然后将容器lt当中的数据,通过遍历的方式一个个尾插到清空后的容器当中即可。

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

写法二:现代写法
现代写法的代码量较少,首先利用编译器机制,故意不使用引用接收参数,通过编译器自动调用list的拷贝构造函数构造出来一个list对象,然后调用swap函数将原容器与该list对象进行交换即可。

//现代写法
list<T>& operator=(list<T> lt) //编译器接收右值的时候自动调用其拷贝构造函数
{
	swap(lt); //交换这两个对象
	return *this; //支持连续赋值
}

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

析构函数

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

//析构函数
~list()
{
	clear(); //清理容器
	delete _head; //释放头结点
	_head = nullptr; //头指针置空
}

迭代器相关函数
begin和end
首先我们应该明确的是:begin函数返回的是第一个有效数据的迭代器,end函数返回的是最后一个有效数据的下一个位置的迭代器。

对于list这个带头双向循环链表来说,其第一个有效数据的迭代器就是使用头结点后一个结点的地址构造出来的迭代器,而其最后一个有效数据的下一个位置的迭代器就是使用头结点的地址构造出来的迭代器。(最后一个结点的下一个结点就是头结点)

iterator begin()
{
	//返回使用头结点后一个结点的地址构造出来的普通迭代器
	return iterator(_head->_next);
}
iterator end()
{
	//返回使用头结点的地址构造出来的普通迭代器
	return iterator(_head);
}

当然,还需要重载一对用于const对象的begin函数和end函数。

const_iterator begin() const
{
	//返回使用头结点后一个结点的地址构造出来的const迭代器
	return const_iterator(_head->_next);
}
const_iterator end() const
{
	//返回使用头结点的地址构造出来的普通const迭代器
	return const_iterator(_head);
}

访问容器相关函数

front和back

front和back函数分别用于获取第一个有效数据和最后一个有效数据,因此,实现front和back函数时,直接返回第一个有效数据和最后一个有效数据的引用即可。

T& front()
{
	return *begin(); //返回第一个有效数据的引用
}
T& back()
{
	return *(--end()); //返回最后一个有效数据的引用
}

当然,这也需要重载一对用于const对象的front函数和back函数,因为const对象调用front和back函数后所得到的数据不能被修改。

const T& front() const
{
	return *begin(); //返回第一个有效数据的const引用
}
T& back()
{
	return *(--end()); //返回最后一个有效数据的引用
}
const T& back() const
{
	return *(--end()); //返回最后一个有效数据的const引用
}

插入、删除函数

insert

insert函数可以在所给迭代器之前插入一个新结点。

在这里插入图片描述 先根据所给迭代器得到该位置处的结点指针cur,然后通过cur指针找到前一个位置的结点指针prev,接着根据所给数据x构造一个待插入结点,之后再建立新结点与cur之间的双向关系,最后建立新结点与prev之间的双向关系即可。

//插入函数
void insert(iterator pos, const T& x)
{
	assert(pos._pnode); //检测pos的合法性

	node* cur = pos._pnode; //迭代器pos处的结点指针
	node* prev = cur->_prev; //迭代器pos前一个位置的结点指针
	node* newnode = new node(x); //根据所给数据x构造一个待插入结点

	//建立newnode与cur之间的双向关系
	newnode->_next = cur;
	cur->_prev = newnode;
	//建立newnode与prev之间的双向关系
	newnode->_prev = prev;
	prev->_next = newnode;
}

erase

erase函数可以删除所给迭代器位置的结点。

在这里插入图片描述先根据所给迭代器得到该位置处的结点指针cur,然后通过cur指针找到前一个位置的结点指针prev,以及后一个位置的结点指针next,紧接着释放cur结点,最后建立prev和next之间的双向关系即可。 

//删除函数
iterator erase(iterator pos)
{
	assert(pos._pnode); //检测pos的合法性
	assert(pos != end()); //删除的结点不能是头结点

	node* cur = pos._pnode; //迭代器pos处的结点指针
	node* prev = cur->_prev; //迭代器pos前一个位置的结点指针
	node* next = cur->_next; //迭代器pos后一个位置的结点指针

	delete cur; //释放cur结点

	//建立prev与next之间的双向关系
	prev->_next = next;
	next->_prev = prev;
	
	return iterator(next); //返回所给迭代器pos的下一个迭代器
}

push_back和pop_back
push_back和pop_back函数分别用于list的尾插和尾删,在已经实现了insert和erase函数的情况下,我们可以通过复用函数来实现push_back和pop_back函数。

push_back函数就是在头结点前插入结点,而pop_back就是删除头结点的前一个结点。
 

//尾插
void push_back(const T& x)
{
	insert(end(), x); //在头结点前插入结点
}
//尾删
void pop_back()
{
	erase(--end()); //删除头结点的前一个结点
}

push_front和pop_front

当然,用于头插和头删的push_front和pop_front函数也可以复用insert和erase函数来实现。

push_front函数就是在第一个有效结点前插入结点,而pop_front就是删除第一个有效结点。

//头插
void push_front(const T& x)
{
	insert(begin(), x); //在第一个有效结点前插入结点
}
//头删
void pop_front()
{
	erase(begin()); //删除第一个有效结点
}

其他函数

size

size函数用于获取当前容器当中的有效数据个数,因为list是链表,所以只能通过遍历的方式逐个统计有效数据的个数。

size_t size() const
{
	size_t size = 0; //统计有效数据个数
	const_iterator it = begin(); //获取第一个有效数据的迭代器
	while (it != end()) //通过遍历统计有效数据个数
	{
		size++;
		it++;
	}
	return size; //返回有效数据个数
}

扩展: 其实也可以给list多设置一个成员变量size,用于记录当前容器内的有效数据个数。

resize
resize函数的规则:

        1.若当前容器的size小于所给n,则尾插结点,直到size等于n为止。
        2.若当前容器的size大于所给n,则只保留前n个有效数据。
实现resize函数时,不要直接调用size函数获取当前容器的有效数据个数,因为当你调用size函数后就已经遍历了一次容器了,而如果结果是size大于n,那么还需要遍历容器,找到第n个有效结点并释放之后的结点。

这里实现resize的方法是,设置一个变量len,用于记录当前所遍历的数据个数,然后开始变量容器,在遍历过程中:

        1.当len大于或是等于n时遍历结束,此时说明该结点后的结点都应该被释放,将之后的结点释放即可。
        2.当容器遍历完毕时遍历结束,此时说明容器当中的有效数据个数小于n,则需要尾插结点,直到容器当中的有效数据个数为n时停止尾插即可。

void resize(size_t n, const T& val = T())
{
	iterator i = begin(); //获取第一个有效数据的迭代器
	size_t len = 0; //记录当前所遍历的数据个数
	while (len < n&&i != end())
	{
		len++;
		i++;
	}
	if (len == n) //说明容器当中的有效数据个数大于或是等于n
	{
		while (i != end()) //只保留前n个有效数据
		{
			i = erase(i); //每次删除后接收下一个数据的迭代器
		}
	}
	else //说明容器当中的有效数据个数小于n
	{
		while (len < n) //尾插数据为val的结点,直到容器当中的有效数据个数为n
		{
			push_back(val);
			len++;
		}
	}
}

empty

empty函数用于判断容器是否为空,我们直接判断该容器的begin函数和end函数所返回的迭代器,是否是同一个位置的迭代器即可。(此时说明容器当中只有一个头结点)

bool empty() const
{
	return begin() == end(); //判断是否只有头结点
}

swap

swap函数用于交换两个容器,list容器当中存储的实际上就只有链表的头指针,我们将这两个容器当中的头指针交换即可。

void swap(list<T>& lt)
{
	::swap(_head, lt._head); //交换两个容器当中的头指针即可
}

注意: 在此处调用库当中的swap函数需要在swap之前加上“::”(作用域限定符),告诉编译器这里优先在全局范围寻找swap函数,否则编译器会认为你调用的就是你正在实现的swap函数(就近原则)。

STL详解— list类的模拟实现内容到此介绍结束了,感谢您的阅读!!!

如果内容对你有帮助的话,记得给我点个赞——做个手有余香的人。感谢大家的支持!!!

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

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

相关文章

推荐系统简介

文章目录 推荐系统的分类基于人口统计学的推荐算法基于内容的推荐算法基于协同过滤的推荐算法协同过滤&#xff08;CF&#xff09;推荐方法基于近邻基于用户的协同过滤基于物品的协同过滤 混合推荐推荐系统实验方法离线实验用户调查在线实验 学习下b站上尚硅谷的推荐系统的课程…

易观千帆 | 2023年4月证券APP月活跃用户规模盘点

易观&#xff1a;2023年4月证券服务应用活跃人数13924.88万人&#xff0c;相较上月&#xff0c;环比下降1.46%&#xff0c;同比增长3.64%&#xff1b;2023年4月自营类证券服务应用Top10 活跃人数6144.02万人&#xff0c;环比下降0.01%&#xff1b;2023年4月第三方证券服务应用T…

「远程开发」VSCode使用SSH远程linux服务器 - 公网远程连接(1)

文章目录 前言视频教程1、安装OpenSSH2、vscode配置ssh3. 局域网测试连接远程服务器4. 公网远程连接4.1 ubuntu安装cpolar内网穿透4.2 创建隧道映射4.3 测试公网远程连接 5. 配置固定TCP端口地址5.1 保留一个固定TCP端口地址5.2 配置固定TCP端口地址5.3 测试固定公网地址远程 转…

chatgpt赋能python:PythonWorkon-使Python多环境管理更加轻松

Python Workon - 使Python多环境管理更加轻松 Python是一种广泛使用的高级编程语言&#xff0c;在众多开发者中应用广泛。在实际工作中&#xff0c;我们有时需要使用不同版本的Python或不同的环境来开发不同的项目。这时候就需要一个Python环境管理器来帮助我们快速地进行环境…

chatgpt赋能python:PythonWSDL:实现更好的Web服务和集成

Python WSDL&#xff1a;实现更好的Web服务和集成 在现代的互联网世界中&#xff0c;我们面临着不断增长的需求&#xff0c;需要实现与不同系统之间的交互和数据共享。Web服务是一种解决方案&#xff0c;允许应用程序通过互联网相互通信。其中Web服务描述语言&#xff08;WSDL…

python+django在线音乐分享网站系统vue_6045i.

本系统主要包括管理员和用户&#xff1a;主要包括个人中心、用户管理、通知公告管理、音乐类型管理、音乐分享管理、音乐榜单管理、音乐论坛、系统管理等功能的管理系统。 (1)绪论 系统的开发背景&#xff0c;意义和系统状况等&#xff0c;详细讲述了系统的用处&#xff0c;对本…

iOS App外包开发解决闪退问题

在iOS应用开发中&#xff0c;闪退&#xff08;应用程序意外退出&#xff09;是一个常见的问题。为了查找和解决闪退问题&#xff0c;可以使用以下工具和方法。今天和大家分享这方面的知识&#xff0c;希望对大家有所帮助。北京木奇移动技术有限公司&#xff0c;专业的软件外包开…

扬州晶澳-年产3GW高性能太阳能光伏组件项目Acrel-3000WEB电能管理系统的设计及应用

摘要&#xff1a;在信息时代&#xff0c;电力信息系统的应用促迚了电力企业的収展&#xff0c;增强了电力系统运行的安全性与稳定性&#xff0c;对满足用户需求其有重要意义。随着国家电网改革政策的逐步推进和落实&#xff0c;Acrel-3000WEB电能管理系统运用互联网和大数据技术…

数据库索引结构(4)---基于B+树的查询

回顾我们学习的线性和课扩展哈希 他们都只能进行等值操作 但是不能进行区间操作 为了进行区间操作和关系的比较和速度考虑 引入数据结构B树 序言:磁盘存储 1. mysql面试题-深入理解B树原理_哔哩哔哩_bilibili 信息是存取在磁盘块中的每个 磁盘最小的存取单位是512KB 要定位磁…

多态的原理、单继承和多继承的虚函数表、以及虚函数表的打印。

一、多态原理 1、下面这个结果是多少&#xff1f; class A { public:virtual void func(){cout << "func()" << endl;}private:int _a 1; };int main() {printf("%d\n", sizeof(A));return 0; } 是 4&#xff1f;8&#xff1f;还是多少&am…

Python星际生存小游戏开发

项目介绍&#xff1a; 项目名称&#xff1a;python星际生存游戏 编程语言&#xff1a;python 用到关键知识&#xff1a;pygame模块&#xff0c;面向对象思想&#xff0c;python基础等等 实现功能&#xff1a; 1&#xff1a;飞机的运动&#xff0c;发射子弹&#xff0c;飞机…

深入探索Apache Flume:大数据领域的数据采集神器【上进小菜猪大数据系列】

&#x1f4ec;&#x1f4ec;我是上进小菜猪&#xff0c;沈工大软件工程专业&#xff0c;爱好敲代码&#xff0c;持续输出干货&#xff0c;欢迎关注。 引言&#xff1a; 随着大数据技术的快速发展&#xff0c;企业和组织需要从各种来源采集海量数据。数据采集是大数据处理流程中…

DTO、VO、BO、PO、DO理解等相关的转换

1、什么是DTO、VO、BO、PO、DO、POJO pojo(domain/entity)一般定义实体类&#xff0c;实体类又被分为VO、BO、 PO、 DTO、DO&#xff1b;通过各层POJO的使用&#xff0c;有助于提高代码的可读性和可维护性。 2、阿里巴巴Java开发规范 DO(Data Object):此对象与数据库表结构一…

chatgpt赋能python:Python的BeautifulSoup库和find_all()方法

Python的Beautiful Soup库和find_all()方法 在Web爬虫中&#xff0c;我们需要从网页中找到特定的HTML标记或属性&#xff0c;以便提取我们需要的数据。对于Python开发人员而言&#xff0c;Beautiful Soup是最流行的解析HTML和XML的库之一。该库可以让我们轻松地从HTML解析器中…

ECMAScript 6 新特性详解

目录 ECMAScript 6 简介 1、箭头函数 2、类 3、增强对象字面量 4、模板字符串 5、解构赋值 6、默认参数、剩余参数、展开操作符 7、let、const 8、迭代器&#xff08;迭代器&#xff09;、for of 9、Generators&#xff08;生成器&#xff09; 10、Unicode 11、模块…

GPC_APDU_Transport_over_SPI-I2C_v1.0_PublicRelease

GPC_APDU_Transport_over_SPI-I2C_v1.0_PublicRelease.pdf 目录 1 简介 越来越多的设备&#xff0c;如移动设备、可穿戴设备或其他 IoT&#xff08;物联网&#xff09;设备现在正在使用焊接安全元件 (SE)。 这产生了支持 SPI 或 I2C 等物理接口的新需求&#xff0c;以代替以前…

高阶python | 字符串高级功能

不可变的字符串 python中的数据类型分为可变的和不可变的两种&#xff0c;字符串属于不可变的数据类型。 不可变的数据类型可以作为字典的键使用&#xff0c;例如&#xff1a; my_dict {Lily: 68, Harry: 98, Lucy: 79} 不可变类型的另一个优点就是有助于进行内部消化&…

ISO_IEC_7816-3

介绍 ISO/IEC 7816 是一系列标准&#xff0c;规定了集成电路卡和此类卡的使用 互换。 这些卡是用于在外部世界和卡中的集成电路之间协商的信息交换的识别卡。 作为信息交换的结果&#xff0c;卡传递信息&#xff08;计算结果、存储的数据&#xff09;和/或修改其内容&#xff0…

高级第一个月考试题

1.什么是Vue框架&#xff1f; Vue是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是&#xff0c;Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层&#xff0c;并且还便于与第三方库或既有项目整合。另一方面&#xff0c;当与现代化的工具链以及各种支持…