【C++】STL学习——list模拟实现

【C++】C++STL详解(四)—— vector的模拟实现 今天我们比较详细地完成了vector类的模拟实现,了解了一些有关的底层原理。接下来,我们将进行STL中list类的学习。希望我的文章和讲解能对大家的学习提供一些帮助。 阅读详情

list介绍

  1. list是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代。
  2. list的底层是双向链表结构,双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向
    其前一个元素和后一个元素。
  3. list与forward_list非常相似:最主要的不同在于forward_list是单链表,只能朝前迭代,已让其更简单高
    效。
  4. 与其他的序列式容器相比(array,vector,deque),list通常在任意位置进行插入、移除元素的执行效率
    更好。
  5. 与其他序列式容器相比,list和forward_list最大的缺陷是不支持任意位置的随机访问,比如:要访问list
    的第6个元素,必须从已知的位置(比如头部或者尾部)迭代到该位置,在这段位置上迭代需要线性的时间
    开销;list还需要一些额外的空间,以保存每个节点的相关联信息(对于存储类型较小元素的大list来说这
    可能是一个重要的因素)

list学习参考文档

list的底层是带头双向循环链表,在学习数据结构时也使用C语言对带头双向循环链表进行过模拟实现——
双向链表的模拟实现;得益于其优秀的结构设计,在数据的插入,删除方面有着极高的效率。

带头双向循环链表

但在STL中的list更让人叫绝的是其迭代器是设计。

  • 迭代器的主要作用就是让算法能够不用关心底层数据结构,对外提供一个统一的访问方式,即使不了解容器的底层是什么,也能通过迭代器对容器进行访问。
  • 迭代器其底层实际就是一个指针,或者是对指针进行了封装,比如:vector的迭代器就是原生态指针T* ,而今天学习的list的迭代器则是一个封装过的类。

list结构介绍

list得益于其结构,实现增删等功能比前面实现的vector,string更加简单。list更值得我们学习的是其迭代器的的实现。而且迭代期也是我们访问list的主要手段,所以优先实现迭代器。

list的实现需要有三个类:节点类,迭代器类,list类。

节点类的实现

节点作为链表组成的基本单位,其成员有:

  • 存储数据的_data
  • 连接前一节点的前驱指针_prev
  • 连接后一节点的后继指针_next

节点类只需要实现默认构造函数即可。负责将成员初始化。

	template<class T>
	struct list_node
	{
		T _data;//数据
		list_node<T>* _prev;//前驱指针
		list_node<T>* _next;//后继指针

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

迭代器的实现

先实现非const版的迭代器;
迭代器的意义就是:

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

而list的底层已不再是连续的空间,而是分散的节点,无法再使用[]加下标访问,也不能仅通过结点指针的自增、自减以及解引用等操作对相应结点的数据进行操作。究其原因就是vector,string的底层空间是连续的,其迭代器就是对应的指针,天然就支持++,–这样的操作。

list的结点指针的行为不满足迭代器定义,那么我们可以对这个结点指针进行封装,对结点指针的各种operator运算符操作进行重载,使其符合迭代器的行为。如:当你使用list当中的迭代器进行自增操作时,实际上执行了p = p->next语句,只是你不知道而已,我们通过类的封装屏蔽了其细节,符合迭代器方法的使用。
总结:
用一个类封装迭代器去模拟指针的行为,该类的成员函数为运算符重载函数(模拟指针行为)。

	template<class T>
	struct __list_iterator//用一个类封装迭代器去模拟指针的行为,该类的成员函数为运算符重载函数(模拟指针行为)
	{
		typedef list_node<T> Node;
		typedef __list_iterator<T> self;
		Node* _node;//节点

		__list_iterator(Node* node)
			:_node(node)
		{}

		self& operator++();
		self& operator--();
		self operator++(int);
		self operator--(int);
		bool operator==(const self& it);//it为迭代器,_node为节点
		bool operator!=(const self& it);
		T& operator*();
		T* operator->();
		
	};

该类中只有一个成员,那就是节点。类名太长了,使用typedef对 __list_const_iterator 重命名为self

构造函数

对于封装的迭代器类,我们只需要实现一个构造函数即可,只需要将获取的链表的节点用来构造迭代器中的节点即可。

	__list_const_iterator(Node* node)
			:_node(node)
		{}

对于我们模拟实现的迭代器,需要明确我们的目的:我们实现的迭代器是为了帮我们去遍历,访问我们的链表。这也是为什么我们不需要实现拷贝构造和赋值重载函数进行深拷贝的原因,我们使用编译器默认生成的浅拷贝的拷贝构造和赋值重载函数就能达到我们的目的,使用深拷贝反而达不到我们但目的。

++运算符重载

对于前置++,使用原则是:先++,再使用;所以直接将当前节点往后走一位,再返回当前节点即可。引用返回效率更好。

		self& operator++()
		{
			_node = _node->_next;
			return *this;
		}

后置++使用原则为:先使用,再++,所以需要返回++之前的值,所以先用一个临时对象保存该值,再让节点往后走,最后返回临时对象。不能引用返回,tmp为临时对象,函数结束就销毁,不能使用引用返回。

	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;
		}

==运算符重载

重载节点与迭代期是否相等。分清_node为节点,而it为迭代期,类型不同;对比的是节点_node和迭代器的成员:节点_node。

        bool operator==(const self& it)//it为迭代器,_node为节点
		{
			return _node == it._node;
		}

!=运算符重载

原理与上述==相同,只不过现在是!=。

	    bool operator!=(const self& it)
		{
			return _node != it._node;
		}

*运算符重载

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

		T& operator*()
		{
			return _node->_data;//返回引用
		}

->运算符重载

在某些场景下,我们需要使用->来访问数据如指针,或对象类型为自定义类型时。
->操作符会先对指针进行解引用,然后访问其指向对象的成员。所以我们只需要返回数据的地址即可完成任务。

		T* operator->()
		{
			return &_node->_data;//返回其地址
		}

注意:
对于->的使用:当数据的类型为自定义类型时,使用一个->就能取到成员对象实际是编译器优化的结果,完整的应为it1.operator->()->对象:先调用operator->()获取对象的地址,再使用->获取对象。
->
好了,以上就是非const版迭代器所需的重载运算符。那么const迭代器如何实现呢,在现有基础上加个const吗?

const迭代器的实现

如果你认为const迭代器是在非const迭代器上加const,那么我们先回顾一下const对指针的相关限制

	int a = 10;
	int b = 20;
	const int* pa1 = &a;//对指针所指向的内容进行限制
	*pa1 = 30;//err,指向的内容不能变。
	pa1 = &b;//可以改变指针变量的指向。

	int* const pa2 = &a;//对指针变量进行限制
	*pa2 = 30;//所指向内容可以改变。
	pa2 = &b;//err,指针变量不能修改。

const迭代器的目的:
list的const迭代器是允许++,–这样的访问操作的,其需要保证的是const对象所指向的内容不能被修改,也就是不能通过运算符重载operator* ()和operator-> ()来改变数据。这么一看不就是类似const int* pa1 = &a;在*pa1前加上const修饰。但是你别忘了,这些都是对原生指针的而言的,而list的迭代器是我们封装的一个类,无论你在哪里加const,都会导致迭代器被const修饰而无法修改,从而连最基本的访问也无法执行了。
在这里插入图片描述
所以list的const迭代器是不可能在非const迭代器变来的,它必须是一个重新的类。

const迭代器:

	template<class T>
	struct __list_const_iterator//用一个类封装迭代器去模拟指针的行为,该类的成员函数为运算符重载函数(模拟指针行为)
	{
		typedef list_node<T> Node;
		typedef __list_const_iterator<T> self;
		Node* _node;

		__list_const_iterator(Node* node)
			:_node(node)
		{}

		self& operator++();
	    self& operator--();
	    self operator++(int);
	    self operator--(int);
	    bool operator==(const self& it);//it为迭代器,_node为节点
	    bool operator!=(const self& it);
	    const T& operator*();
		const T* operator->();
	};

仔细一看两个迭代器,虽说是两个不同的类,但是内容高度重合,因为const迭代器不能通过运算符重载operator*()和operator->()来改变数据,所以我们只需要对这两个函数的返回值加const限制即可。逻辑也是一致的。

		const T& operator*()
		{
			return _node->_data;//返回引用
		}

		const T* operator->()
		{
			return &_node->_data;//返回其地址
		}

多参数模板迭代器

对比const迭代器和非const迭代器,你会发现只有operator*()和operator->()处多了一个const,就因为这两个const而重写了一份一模一样的代码,这不是为了吃醋而包饺子了嘛。为了解决这种重复代码的问题,C++拿出了模板来应对。

const迭代器和非const迭代器的代码中有不同的只有两处:T&与const T&,T与const T;再加上原本的T,我们将模板参数设为三个。template<class T,class Ref,class Ptr>,T为数据类型,Ref为T&或const T&,Ptr为T*或const T*,再将operator*(),operator->()的类型更为Ref和Ptr即可在使用时根据所需要的迭代器类型来实例化所需的代码。

template<class T,class Ref,class Ptr>//用一个类封装迭代器去模拟指针的行为,该类的成员函数为运算符重载函数(模拟指针行为)
struct __list_iterator
{
	typedef list_node<T> Node;
	typedef __list_iterator<T,Ref,Ptr> self;
	Node* _node;

	__list_iterator(Node* node)
		:_node(node)
	{}

	self& operator++();
	self& operator--();
	self operator++(int);
	self operator--(int);
	bool operator==(const self& it);
	bool operator!=(const self& it);
	Ref operator*()const;//返回数据的引用
	Ptr operator->()const;//返回数据的地址
	
};

在list类当中,只需要使用对应的迭代器,模板参数的Ref就会替换成const T&或者T&,Ptr就会替换为const T*或者T*,这样就能使用同一份类模板实例化出两种迭代器。

	template<class T>
	class list
	{
		typedef list_node<T> Node;

		//构造时以下操作多,设为私有不公开
		void list_init()//开好头节点
		{
			_head = new Node;
			_head->_next = _head;
			_head->_prev = _head;
		}

	public:
		typedef __list_iterator<T,const T&,const T*> const_iterator;//使用那个迭代器就实例化哪一个迭代器
		typedef __list_iterator<T,T&,T*> iterator;

   };

好了,这就是迭代器的模拟实现,接下来进入list的模拟实现。

list函数接口总览

list类的结构十分简单,只有一个节点类;还记得我们list的底层是带头双向循环链表吗,所以这里的成员_head为头节点,不存储有效数据。

	template<class T>
	class list
	{
		typedef list_node<T> Node;
		Node* _head;//头节点

		//构造时以下操作多,设为私有不公开
		void list_init()//开好头节点
		{
			_head = new Node;
			_head->_next = _head;
			_head->_prev = _head;
		}

	public:
		typedef __list_iterator<T,const T&,const T*> const_iterator;//使用那个迭代器就实例化哪一个迭代器
		typedef __list_iterator<T,T&,T*> iterator;

		//typedef __list_iterator<T> iterator;
		//typedef __list_const_iterator<T> const_iterator;

		list();
		list(initializer_list<T> il);
		template <class InputIterator>
		list(InputIterator first, InputIterator last);
		list(const list& x);
		list& operator= (const list& x);
		~list();
		iterator begin();
		iterator end();
		const_iterator begin()const;
		const_iterator end()const;
		iterator insert(iterator position, const T& val);
		void push_back(const T& val);
		void push_front(const T& val);
		iterator erase(iterator position);
		void pop_back();
		void pop_front();
		bool empty() const;
		size_t size() const;
		void swap(list& lt);
		T& front();
		const T& front() const;
		T& back();
		const T& back() const;
		
    };
  • 注意,模拟实现的节点类,迭代器类,lsit类为了不和库的发生冲突,全都放在自己的命名空间里。

默认成员函数

我们实现了三个构造函数:

  • 第一个为默认构造
  • 第二个为迭代器区间构造
  • 第三个为使用initializer_list来构造。

构造函数开头节点的操作都一致,所以将这份代码装一起,并设为私有。

		//构造时以下操作多,设为私有不公开
		void list_init()//开好头节点
		{
			_head = new Node;
			_head->_next = _head;
			_head->_prev = _head;
		}

构造函数1

默认构造,开好头结点即可。

		list()
		{
			list_init();//开好头节点
		}

构造函数2

使用迭代器区间构造,开好头节点后,将传入区间的元素一个一个push_back进容器当中。

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

构造函数3

使用initializer_list来构造,这种方法是在C++11提出的,使用方法如下:
直接将所要用添加的元素放在花括号里。

	list<int>lt6 = { 1,2,3,4,5,6 };
	list<int>lt7({ 1,2,3,4,5,6 });

这里的initializer_list是一个类模板,支持迭代器的使用。
initializer_list
initializer_list支持迭代器,所以使用范围for直接将initializer_list中的数据push_back进容器

		list(initializer_list<T> il)
		{
			list_init();
			for (const auto& e : il)
			{
				push_back(e);
			}
		}

析构函数

我们一共实现了三个类,但前两个都没有实现析构函数,那是因为前两个都没有实现深拷贝的拷贝构造和赋值重载函数;而且就像迭代器类,你总不能访问完就把我的节点给释放了吧。所以我们只需要在进行了深拷贝的list中写析构函数。
实现也简单,使用clear先将存储有效数据的节点都释放,最后再将头节点释放。

  • clear是不清除头结点的。
		~list()
		{
		    clear();
			delete _head;
			_head->_next = _head->_prev = nullptr;
		}

拷贝构造函数

有了之前vector,string的经验,现在实现拷贝构造也应该信手拈来了:开好头节点,然后使用范围for直接将数据push_back进容器即可。


		list(const list<T>& lt)
		{
			list_init();
			for (const auto& e : lt)
			{
				push_back(e);
			}
		}

赋值重载函数

使用现代写法,利用不用引用传参的形参会在传参时调用拷贝构造,构造一个一模一样的list对象,然后调用swap函数将原容器与该list对象进行交换即可。

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

迭代器

迭代器的使用区间为:左闭右开,所以开始位置应该是第一位有效数据——即头结点的下一位;而结尾则应该是不存储有效数据的头节点。

  • 返回类型为迭代器,但返回的是节点,这里其实发生了隐式类型转化,编译器在返回时会用该节点构造一个迭代器。
  • 也可以使用匿名构造,直接构造一个迭代器类型。

begin()和end()

	    iterator begin()
		{
			return _head->_next;
			//return iterator(_head->_next);
		}

		iterator end()
		{
			return _head;
		}

非const迭代器为const对象所调用,防止对容器内容进行修改

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

修改相关函数

insert

使用迭代器在指定位置插入值val:首先使用val创建一个新的节点,紧接着让pos前后位置的前驱或后继指针建立新的双向关系。具体如下:
insert

		iterator insert(iterator position, const T& val)
		{
		    assert(position._node);//防止空指针
			Node* node = new Node(val);
			Node* pos = position._node;//position为迭代器类型,需要获其节点

			node->_prev = pos->_prev;
			node->_next = pos;

			pos->_prev->_next = node;
			pos->_prev = node;
			return node;
		}
  • list中进行插入时是不会导致list的迭代器失效的

push_back和push_front

调用insert,在头或尾插入val;

		void push_back(const T& val)
		{
			insert(end(), val);
		}

		void push_front(const T& val)
		{
			insert(begin(), val);
		}

erase

删除指定位置的数据:需要对传入的迭代器进行合法性审查,防止空指针,而且不能删除头节点;然后删除位置前后的节点重新建立双向关系;删除指定位置节点时记得先保存下一节点,用作返回。
erase

		iterator erase(iterator position)
		{
			assert(position._node);//防止空指针
			assert(position != end());//不能释放头节点
			assert(!empty());//非空才能删
			Node* pos = position._node;//节点

			pos->_next->_prev = pos->_prev;
			pos->_prev->_next = pos->_next;

			iterator ret = pos->_next;
			delete pos;//new的时节点,所以delete节点
			return ret;
		}

erase会有迭代器失效问题,使用前记得更新获取迭代器位置。

pop_back和pop_front

同样调用erase完成任务,pop_front删除头节点的下一位,pop_back删除头节点的前一位——即最后一位。

  • 尾删是删除存储有效数据的节点,end返回的是头节点。
		void pop_back()
		{
			erase(--end());
		}

		void pop_front()
		{
			erase(begin());
		}

empty

检查链表是否为空:对于空的链表而言,只剩下一个头节点,此时_head的_prev和_next都指向_head,因此可以利用这一点来判断链表是否为空。

empty

		bool empty() const
		{
			return _head->_next == _head;
		}

size

获取容器中数据个数:用size来统计个数,再利用范围for遍历链表。

		size_t size() const
		{
			size_t size = 0;
			for (auto e : *this)
			{
				size++;
			}
			return size;
		}

swap

交换链表:交换两个链表的头节点即可。

		void swap(list& lt)
		{
			std::swap(_head, lt._head);
		}

clear

清空链表:除头节点外,清空剩余全部节点;借助erase,删除节点。

  • erase会返回删除节点的下一个节点,所以不需要++,只需要接受其返回值即可。
		void clear()
		{
			auto it = begin();
			while (it != end())
			{
				it = erase(it);//erase返回下一个节点
			}
		}

访问相关函数

front和back

访问一头一尾的数据,直接使用*迭代器的操作访问一头一尾的操作。


		T& front()
		{
			return *begin();
		}

	    T& back()
		{
			return *(--end());
		}

当然少不了const版,因为const对象调用front和back函数后所得到的数据不能被修改。

        const T& front() const
		{
			return *begin();
		}

		const T& back() const
		{
			return *(--end());
		}
		

至此,list的模拟实现基本完成了。

模板的拓展应用

如果我们想要在类外实现一个list的打印函数:利用迭代器可以轻松完成。

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

但这样写只是针对list的类型为int,换做其他类型就不行了,所以我们可以套上模板,支持所有list的打印。
注意:
由于我们套上了模板,这样的话在编译时list的类型是不确定的,所以迭代器也不能确认,所以在list<T>::iterator it = lt.begin();处会报错,无法通过编译。此时可以加上前缀typename作为标记,让编译器先通过编译,等模板实例化后再取。

	template<class T>
	void print_list(list<T>& lt)
	{   //由于list<T>还未实例化,所以T的类型不确定,编译不通过。
	    //此时在迭代器前加上typename,作为标记,让编译器编译成功,等实例化后再来确定迭代器具体类型
		typename list<T>::iterator it = lt.begin();
		while (it != lt.end())
		{
			cout << *it << " ";
			it++;
		}
		cout << endl;
	}

除此之外,还能实现一个针对所有容器都能打印的函数吗?
我们此时直接将具体的类作为模板参数,传入的是哪种容器就去取该容器的迭代器,从而进行打印。


	template<class Container>//格局打开,直接将具体的类作为模板参数
	void print_containers(Container& con)
	{
		typename Container::iterator it = con.begin();
		while (it != con.end())
		{
			cout << *it << " ";
			it++;
		}
		cout << endl;
	}

本篇篇幅有点长,主要对list的迭代器进行介绍及模拟实现,对于不是原生指针的迭代器,其const的迭代器使用了多参数模板进行实现,一份代码实例化两种迭代器,这也是本篇博客的难点及精华,对于模板的学习,需要在日后的实践中不断学习。出错的地方还请指出。Thank~

Arduino开发实例-EMG 肌肉信号传感器 在医学研究中,测量肌肉的活动、收缩和扩张非常重要。 EMG 肌肉传感器测量肌肉活动并产生信号以显示扩张和收缩量。 因此,输出取决于所选肌肉的活动量。 3 绿色、红色和黄色电极连接到模块,用于传输来自肌肉运动的电信号。 阅读详情

相关推荐

开源PACS系统Orthanc(DICOM服务器)配置

PACS(Picture Archiving and Communication System)是医学影像的数字化存储、检索和传输系统,用于管理和存储医学图像和相关信息,如放射学、核医学、超声、病理学等各种医学影像。PACS系统通过数字化技术取代了传统的胶片方式,使医疗机构能够更高效地存储、传输和查看医学图像,从而提高了医疗图像的管理和工作流程。

qq_42231043的博客 6250

C++ STL学习之【list的模拟实现】

STL中的list是一个双向带头循环链表,作为链表的终极形态,各项操作性能都很优秀,尤其是list中迭代器的设计更是让人拍案叫绝,如此优秀的容器究竟是如何实现的?本文将带你共同揭晓

北 海的博客 5051

佳能C3025打印机扫描方法

佳能C3025彩色打印机扫描方法 步骤一、如果打印机处于休眠状态,可按图中标记的按钮唤醒打印机; 然后点击屏幕上的【扫描】 步骤二、扫描类型选择【文件】 步骤三、点击屏幕上的【地址簿】 步骤四、此时屏幕胡看到名字,如果看不到自己的名字,可往下滑动直到看到自己的名字,然后点击自己的名字,再点击右下角的【应用】 步骤五、点击【发送设置】,可看到文件格式为PDF,点击可以选择JPEG的图片格式。...

weixin_46019317的博客 2万+

【C++】STL学习之list的模拟实现

STL中的list是一个双向带头循环链表,作为链表的终极形态,各项操作性能都很优秀,尤其是list中迭代器的设计更是让人拍案叫绝,如此优秀的容器究竟是如何实现的?本文将带你共同揭晓。

Aron的博客 1306

【华为OD机试真题 2025双机位A卷】958、 高效的任务规划 | 机试真题+思路参考+代码解析(C++、Java、Py、C语言、JS)

【华为OD机试真题 2025双机位A卷】958、 高效的任务规划 | 机试真题+思路参考+代码解析(C++、Java、Py、C语言、JS) 华为od机试,华为od机试真题,华为od机试E卷,华为odE卷,华为od机试真题E卷,华为OD,华为OD机试,华为OD机试真题,华为od机考,华为od机试D卷,华为od机试真题D卷,华为od手撕代码,华为od,华为od机试C卷,华为od e卷,华为2025e卷,od机试,2025E卷,华为OD机试E卷,什么是华为od,华为od介绍,全面了解华为od,2025华为od,

KJ.JK 1074

HBase批量导入工具:BulkLoad使用详解

HBase作为分布式NoSQL数据库,在大数据领域有着广泛应用。传统的数据写入方式(如Put操作)在面对海量数据导入时存在性能瓶颈。BulkLoad提供了一种高效的大批量数据导入方案,可以显著提高数据加载速度并降低集群负载。BulkLoad的核心原理和架构与传统写入方式的性能对比详细的使用方法和实现步骤性能优化技巧和最佳实践实际应用案例分析本文首先介绍BulkLoad的基本概念和原理,然后深入讲解其实现机制和关键技术。接着通过实际案例演示BulkLoad的使用方法,并分析性能优化策略。

大数据洞察的博客 1236

C++学习之STL学习:list的模拟实现

数据结构学习之双向循环链表-优快云博客),因为这个类中的成员我们都想要访问,如果想要方便写的话,可以用struct(struct中默认为公开),并且用命名空间封装//当我们不需要也不想要让访问限定符限制的时候//可以用struct来定义类,而不是用classT _data;//数据//指向下一个节点的指针//指向前一个节点的指针同时因为list模拟实现中使用了模板,所以函数的定义也只能在.h文件中()同时切记,模板只能给对应的类或函数使用,是“一次性”的,想再次使用一样的模板必须重新定义。

2401_89119815的博客 981

人工智能 (Artificial Intelligence, AI) 原理与代码实例讲解

人工智能 (AI) 机器学习 (Machine Learning) 深度学习 (Deep Learning) 算法 模型 代码实例 应用场景 1. 背景介绍 人工智能 (Artificial Intelligence, AI) 作为一项前沿科技,近年来发展迅速,已渗透到生活的方方面面

AI架构师小马 1228

C++学习之STL:list的模拟实现与解析

STL之list一级目录:list的介绍二级目录:list接口三级目录:list的模拟实现 一级目录:list的介绍 1.在数据结构中,我们都学过list是一个链表,有很多分类,比如单向链表,双向链表,循环链表等等…,但在STL中,list是一个容器,毋庸置疑,是用来保存链表与数据结构中的list有相似之处,但是在实现的时候会更加的绝妙,让人佩服。 首先,我们先看看文档中对STL中list的介绍: list是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代。 list的底层

xiaobai_hellow的博客 1238

第一章 EB安装

1.DemoWorkspace 是基于EB当前组件配置下的例程,包含了启动文件和中断服务函数以及组件demo。2.Mclsar 下存放的是EB的插件包和静态MCAL的驱动文件。

舊梦的博客 1963

【SpringBoot 3.x 第56节】再见 Feign?原生 HTTP 接口强势上位,这才是微服务调用的正确姿势!

🏆本文收录于《滚雪球学SpringBoot 3》,专门攻坚指数提升,本年度国内最系统+最专业+最详细(永久更新)。    本专栏致力打造最硬核 SpringBoot3 从零基础到进阶系列学习内容,🚀均为全网独家首发,打造精品专栏,专栏持续更新中…欢迎大家订阅持续学习。 如果想快速定位学习,可以看这篇【SpringBoot3教程导航帖】,你想学习的都被收集在内,快速投入学习!!两不误。    若还想学习更多,可直接前往《滚雪球学SpringBoot(全版本合集)》,涵盖SpringBoot所有版本教学文章

**My Coding Family** 1017

[C++——lesson19.STL 学习——「list的模拟实现」]

本文深入剖析了STL中list容器的实现原理,重点讲解了双向循环链表的设计思路和迭代器类的实现细节。文章首先介绍了list的三个核心组件:节点类、迭代器类和链表类的基本框架,详细阐述了节点指针的链接关系。随后着重分析了迭代器类的设计,包括多参数模板的巧妙运用、双向移动策略的特殊处理以及operator->()的实现原理。文章还展示了list的核心功能实现,如头尾插删、任意位置插删、容量访问等操作,并通过代码示例演示了现代写法在拷贝构造和赋值重载中的应用。最后简要介绍了交换、调整和清理等辅助功能的实现方

2401_83386596的博客 626

29、Power BI 中行级安全(RLS)的实现与应用

本文详细介绍了在Power BI中实现行级安全(RLS)的方法,包括静态RLS和动态RLS的实现流程、适用场景及对比分析。通过具体业务场景,展示了如何创建安全角色、定义规则、验证角色、发布模型以及分配成员。此外,还探讨了更复杂的动态RLS场景,如基于父子层次结构的数据访问控制和从外部源获取用户信息的实现方式。文章最后总结了最佳实践建议及未来发展趋势,为企业在Power BI中高效、安全地实施数据访问控制提供了全面指导。

terraform7cloud的博客 195

C++初阶学习————STL (list使用及简单的模拟实现)

C++初阶学习

hxMGE的博客 558

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

C++初阶学习——探索STL奥秘——模拟实现list类

list模拟实现

Joker10085的博客 1624

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

【C++ STL学习之路】 ---list重点以及模拟实现

list的迭代器 迭代器失效 模拟实现

Zhou000815的博客 235

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

【C++】list模拟实现

💗💗⭐个人专栏——⭐💫点击关注🤩一起学习C语言💯💫。

qq_64818885的博客 2165

(超详细 + 逐个解读)C++的STL学习 8 : list 的模拟实现 (具体实现)

上次我们学习了list容器的各种接口的使用, 那么按照惯例, 这次我们就来模拟实现一个自己的list, 方便我们对list容器有更深层次的理解. 那么话不多说, 直接开整. 文章目录一. list的结构描述 一. list的结构描述 list是一个带头双向循环链表, 首先它是一个链表, 所以我们要封装一个结点的结构体 当然这个结点也是模板, 里面有两个指针和一个数据, 并且有一个构造函数 //结点 template <class T> struct ListNode { T _data.

Monster的博客 191

C++效率掌握之STL库:list底层剖析及迭代器万字详解

list 的函数用法与 STL 库中其他的大差不差,本文章难度有些上升,将针对前面忽略的迭代器和模版进行深度解析,真正了解到底什么是迭代器,和迭代器的实现原理

Zero_VPN的博客 2473

【C++_stl篇】5万字超强总结C++STL三种常见容器string & vector & list的熟练使用,模拟实现和练习题型

字符串最后一个单词的长度_牛客题霸_牛客网string str;if(i!else {vector相当于我们以前学习数据结构章节时的顺序表。1. vector是表示可变大小数组的序列容器。2. 就像数组一样,vector也采用的连续存储空间来存储元素。也就是意味着可以采用下标对vector的元素进行访问,和数组一样高效。但是又不像数组,它的大小是可以动态改变的,而且它的大小会被容器自动处理。3. 本质讲,vector使用动态分配数组来存储它的元素。

2302_78684687的博客 1654

【C++强基篇】学习C++就看这篇--->STL之stack、queue、deque使用及实现

本篇博客详细介绍了stack、queue、deque的使用及实现,对于重要接口进行了剖根问底的解释,看完你定有收获!

熬夜秃顶中...... 1424

C++ STL容器深度解析与应用实践

实际使用中发现,平台提供的即时代码执行和环境隔离功能,让STL学习过程更加直观高效。特别是部署容器演示项目时,无需配置复杂环境即可获得完整可交互的示例。resize()会改变size并初始化新增元素。1. 实时测试不同容器的性能差异。2. 通过可视化调试观察内存变化。list删除仅使当前迭代器失效。避免memcpy导致浅拷贝问题。3. 一键分享容器模拟实现代码。写时拷贝可优化多副本场景。

StarfallOwl89的博客 304

【C++】stack与queue的使用与模拟实现

在这里插入图片描述也就是说,它的底层,可以是已有的容器,比如:list、vector…… 关于容器适配器我会在下篇博客中来详细阐述,敬请期待,现在让我们来学习stack与queue吧。代码语言:javascriptAI代码解释这个题很简单,但是我们要来体会C++中STL的方便,假设我们用C语言来完成的话,首先要自己实现一个栈才可以解题,但C++不需要。参考题解:代码语言:javascriptAI代码解释有兴趣的自己也可以练练,STL我们要多使用才可以掌握,多练。,有时间也可以练练。

m0_74824865的博客 399

【C++闯关笔记】STL:list 的学习和使用

本文介绍了C++标准库中的list容器,重点阐述了其作为双向链表的特性及优势。文章首先解释了list的引入背景(解决vector中间插入/删除效率低的问题),并通过火车比喻形象说明其结构特点。接着详细讲解了list的使用方法,包括头文件包含、声明初始化、常用接口(增删查改)及迭代器使用注意事项。最后深入剖析了list的模拟实现过程,展示了节点结构、迭代器设计、核心成员函数等关键代码实现。全文通过理论讲解与代码实践相结合的方式,帮助读者全面理解list容器的工作原理和适用场景。

Kaisir777的博客 842

C++ vector底层详解|小白也能看懂的容器模拟实现

vector的基本框架。

2601_95175861的博客 777

第十六届蓝桥杯大赛软件赛省赛C/C++ 研究生组省赛-5(模拟)

【代码】第十六届蓝桥杯大赛软件赛省赛C/C++ 研究生组省赛-5。

ck的博客 36
上一篇: 【C++】STL学习——vector模拟实现
下一篇: 【C++】STL学习——stack和queue的讲解(了解适配器)
Mesar_
博客等级 码龄3年 926粉丝 56原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值