c++ List 接口的基本实现 主要包含《List的使用》《iterator的使用》《capacity》《element access》《modifiers》《迭代器失效》

一 ,List的介绍和使用

1。 List的介绍

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

2 List的使用

List的接口是非常的多的,我们可以在网页上面查找它使用的接口,下面是常用的使用接口

(1)List的构造函数
在这里插入图片描述
下面看一下,下面的代码:

int main()
{
	list<int> l1;                                //构造空的l1;
 	list<int> l2(4,100);                         //l2中放4个值为100的数;
	list<int> l3(l2.begin(),l2.end());           //用l2的左右区间开辟l3;
	list<int> l4(l3);                            //用l3拷贝构造l4;
int arr[] = { 1, 9, 7,5 };
	list<int> l5(arr, arr + sizeof(arr) / sizeof(int));
//使用迭代器打印l5中的成员
	for (list<int>::iterator it = l5.begin(); it != l5.end(); ++it)
		cout << *it << " ";
		cout << endl;
	for (auto&e : l5)
		{
			cout << e << " ";
		}
		cout << endl;
	return 0;
}

这些就是List的基本实现所含有的内容,下面开始正式开始我们的学习。

(2)list iterator的使用

此处,大家可暂时将迭代器理解成一个指针,该指针指向list中的某个节点。
在这里插入图片描述
【注意】

  1. begin与end为正向迭代器,对迭代器执行++操作,迭代器向后移动
  2. rbegin(end)与rend(begin)为反向迭代器,对迭代器执行++操作,迭代器向前移动
  3. cbegin与cend为const的正向迭代器,与begin和end不同的是:该迭代器指向节点中的元素值不能修改
  4. crbegin与crend为const的反向得带器,与rbegin和rend不同的是:该迭代器指向节点中的元素值不能修改

下面我们就实现一下,下面的代码

int main()
{
	int arr[] = { 1, 2, 3, 5, 6, 9, 8, 7, 4, 0 };
	list<int> l(arr, arr + sizeof(arr) / sizeof(int));
for (list<int>::iterator it = l.begin(); it != l.end(); it++)
		cout << *it << " ";
	cout << endl;

for (auto& e : l)
	{
		cout << e << " ";
     }
	cout << endl;

//反向迭代器
	for (list<int>::reverse_iterator it = l.rbegin(); it != l.rend(); ++it)
		cout << *it << " ";
	    cout << endl;
	return 0;
}

运行结过为下面的代码:
在这里插入图片描述
(3)list capacity
在这里插入图片描述
下面是我们的代码实现:

int main()
{
	int arr[] = { 1, 2, 3, 5, 6, 9, 8, 7, 4, 0 };
	list<int> l(arr, arr + sizeof(arr) / sizeof(int));

//答应该字符里面的个数
	cout << l.size() << endl;
//判断是否为空
	if (l.empty())
	{
		cout << "list = nullptr" << endl;
	}
	else
	{
		for (auto& e : l)
		{
			cout << e << " ";
		}
		cout << endl;
	}
	return 0;
}

运行结果我们就不看了,可以实现;

(4)list element access // 列表元素访问
在这里插入图片描述
下面我们看一下代码的实现;

int main()
{
	int arr[] = { 1, 2, 3, 5, 6, 9, 8, 7, 4, 0 };
	list<int> l(arr, arr + sizeof(arr) / sizeof(int));

	

 l.front() = 10;
	 l.back() = 10;
	 
	for (auto& e : l)
	{
		cout << e << " ";
	}
	cout << endl;

	const list<int> l1(arr, arr + sizeof(arr) / sizeof(int));
	//int&cl = l1.front(); //这里会报错,我们可以看上面为const的类型,所以我们需要使用const; 

	const int&cl = l1.front(); //我们可以看到,这里可以编过;

return 0;
}

程序执行结果和上面的差不多;

(5)list modifiers //调节器,修饰语

//这个实现的程序比较多的接口实现,比如(list的头插,头删,尾插,尾删,等等…),所以我准备另写一篇博客,把它实现以下,链接放在下面;

链接https://blog.csdn.net/dpfxaca6/article/details/89222644

(6)list 的迭代器失效

前面说过,此处大家可将迭代器暂时理解成类似于指针,迭代器失效即迭代器所指向的节点的无效,即该节点被删除了。因为list的底层结构为带头结点的双向循环链表,因此在list中进行插入时是不会导致list的迭代器失效的,只有在删除时才会失效,并且失效的只是指向被删除节点的迭代器,其他迭代器不会受到影响。

看下面的一段代码,来理解我们的list迭代器失效的问题:

int main()
{
	int arr[] = { 1, 2, 3, 5, 6, 9, 8, 7, 4, 0 };
	list<int> l(arr, arr + sizeof(arr) / sizeof(arr[0]));
/*auto it = l.begin();
while (it != l.end())
{
l.erase(it);   //erase()执行之后,it节点已经释放了,在访问it时,已经无效了。所以应该先先给赋值
++it;
}
cout << endl;*/

auto it = l.begin();
while (it != l.end())
{
	l.erase(it++);   //注意:这里是后置++,使用前置++,程序崩溃,找不到前面的那个;
}
cout << endl;
return 0;
}

程序的运行结果:
执行上面的代码,程序直接就崩溃了:
在这里插入图片描述

后面的就可以直接运行了,希望大家可以明白

猜你喜欢

转载自blog.csdn.net/dpfxaca6/article/details/89190861