list的实现

一.push_back

复制代码
	void push_back(const T& x)
	{
		Node* newnode = new Node(x);
		Node* tail = _head->_prev;

		tail->_next = newnode;
		newnode->_next = _head;
		newnode->_prev = tail;
		_head->_prev = newnode;
		++_size;

	} 

对于类模板,在类外写类型时不想要写的那么麻烦,其实可以这么干:

二.迭代器的实现

1.普通迭代器

定义类时,struct默认成员变量以及函数是公有 ,而class 默认是私有

链式存储每个节点间的地址并不连续,无法再像vector和string那样用原生指针代替迭代器。但可以通过单独声明一个iterator类**(用struct而非class,因为解引用要在外部能使用)**,以及在类里重载"*"以及"++","--","!=","==",以实现类似vector和string那样用原生指针代替迭代器。

重载"*"返回的是节点的_data:

重载"++":把下一个节点赋值给当前节点,以实现向后的效果,并返回节点的迭代器。

这么做就相当于迭代器指向下一个节点。

2.const_iterator

①.是const_iterator而不是const iterator

const修饰的对象是自身无法修改,而非所指向的内容无法修改。这里的const迭代器所要实现的,是使迭代器所指向的内容无法修改,而非迭代器本身无法修改,所以要单独实现一个迭代器,而非单纯在iterator前添加一个const那么简单。

②.具体实现

重新声明一个const_iterator类,并在某些接口后增加一个const。最后在list类模板里再重载一下

begin()和end()。

复制代码
template<class T>
struct list_const_iterator
{
	typedef node<T> Node;
	typedef list_const_iterator<T> self;
	Node* _node;
	list_const_iterator(Node* node = T())
		:_node(node)
	{
	}
	const T& operator* ()
	{
		return _node->_data;
	}
	const T* operator->()
	{
		return &_node->_data;
	}
	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;
	}
	bool operator!= (const self& s) const
	{
		return this->_node != s._node;
	}
	bool operator== (const self& s) const
	{
		return this->_node == s._node;
	}
};

重载的begin和end:

三.重载->

为了使迭代器像对象的指针访问 对象的成员变量那样,需要重载一个"->"。

奇奇怪怪???

为啥重载的"->"返回的是一个地址呢?

重载其实是两个箭头

第一个 "->"是重载的运算符,返回指针。

第二个"->"是系统自带的,用于访问结构指针解引用并访问结构里的元素。

注:这里是向lta尾插了几个匿名对象 ,所以_data是个对象。因此有了_data的地址,就可以用 "->"访问这个对象里的元素。

四.迭代器失效

1.insert

list的insert并不会迭代器失效,原因在于insert以后it仍旧指向原空间。

2.erase

会失效,因为erase以后,it指向的空间会被释放,如果对it++,就相当于是对野指针++,那么it就是失效的。

五.析构函数

六.拷贝构造

以l2,l1为例,范围for会编译不通过,原因在于l2这个变量并未被初始化,没有哨兵位,或者哨兵位的指向并不满足初始化的形式(_head->_next = _head,_head->_prev = _head),这样的l2是无法使用push_back的。所以需要写一个empty_init接口来实现初始化。

自己遇到的问题

1.在typedef迭代器的时候忘记带T。迭代器是个类模板,而使用类模板需要传递模板参数!!

更改后:

2.node类模板里一定要带上构造函数。

构造函数(带缺省值):

其中缺省值传匿名对象,会去调用相应类型的默认构造函数,对于内置类型:int就给0,指针就给nullptr,以实现通用性。

积累经验:出现下面这种报错,就说明缺乏默认构造。

由于缺乏默认构造,系统会自动将语句识别为转换,所以实际的错误是没有默认构造。

3.打印接口不支持范围for

其实是打印接口中const的问题。范围for的底层是迭代器,由于形参con是const类型的,那么就应该替换const类型的迭代器。而我们实现的是普通类型的迭代器,所以这里的范围for是不支持的。需要去实现const迭代器。

相关推荐
噢,我明白了2 天前
Java 入门,详解List,Map集合使用
java·list·map
小马_xiaoen2 天前
前端虚拟列表(Virtual List)从原理到实战:海量数据渲染终极方案
前端·数据结构·list
Jul1en_3 天前
【Redis】List列表命令、编码方式及应用场景
数据库·redis·list
小毛驴8504 天前
在Java高并发环境下,实现线程安全的List和Map有以下几种常见方案
java·安全·list
王璐WL4 天前
【C++】string,vector和list对比
c++·list
计算机安禾5 天前
【数据结构与算法】第45篇:跳跃表(Skip List)
c语言·数据结构·算法·list·排序算法·图论·visual studio
Severus_black8 天前
C实现双向链表和相关函数!巨详细!
c语言·数据结构·链表·list
Magic--8 天前
C++ STL中vector与list的核心区别
c++·windows·list
li1670902709 天前
第十章:list
c语言·开发语言·数据结构·c++·算法·list·visual studio
游乐码9 天前
C#List
开发语言·c#·list