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迭代器。

相关推荐
Kiling_070415 小时前
Java集合进阶:Collection与List详解
java·windows·list
XS0301062 天前
Java基础 List集合
java·windows·list
北风朝向2 天前
springboot使用@Validated校验List接口参数
spring boot·后端·list·校验·valid
Rabitebla2 天前
从零实现 C++ List:带头循环双向链表的每一个细节
数据结构·c++·算法·leetcode·链表·list
庞轩px3 天前
第一篇:Redis数据结构底层——String、List、Hash、Set、ZSet各自用什么实现的?
数据结构·redis·list·set·hash·string·zset
Brilliantwxx3 天前
【C++】认识 list(初步认识+模拟实现)
开发语言·数据结构·c++·笔记·算法·list
Emberone3 天前
C++ list 详解:从入门到模拟实现,彻底搞懂双向链表
c++·list
装杯让你飞起来啊5 天前
Kotlin List / Array 与 for 循环
开发语言·kotlin·list
qq_589568106 天前
springbootweb案例,出现访问 http://localhost:8080/list 一直处于浏览器运转阶段
java·网络协议·http·list·springboot
小则又沐风a7 天前
list模拟实现
java·服务器·list