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

相关推荐
临溟夜空的繁星1 天前
C++STL—— list
开发语言·c++·list
旺仔.2912 天前
顺序容器:forward list单链表 详解
数据结构·c++·list
Zzj_tju2 天前
Java 从入门到精通(九):集合框架入门,List、Set、Map 到底该怎么选?
java·开发语言·list
旺仔.2913 天前
顺序容器:list双向链表 详解
数据结构·c++·链表·list
_MyFavorite_4 天前
JAVA重点基础、进阶知识及易错点总结(8)List 接口(ArrayList、LinkedList、Vector)
java·开发语言·list
是娇娇公主~4 天前
C++ 中 std::vector 和 std::list 的区别
开发语言·c++·list
刘大猫.5 天前
java工具:《字符串转List》
list·json解析·fastjson·反序列化·jsonobject·jsonarray·typereference
宝耶6 天前
Java面试题5:List、Set、Map 的区别?各自有哪些实现类?
java·开发语言·list
你真是饿了8 天前
10.list
c++·list