C++知识点22——使用C++标准库(顺序容器list的初始化、赋值、访问、交换、添加、删除与迭代器失效)

list容器是双向链表,使用前,需要添加#include <list>

1.list的初始化

常用的构造函数如下

explicit list (const allocator_type& alloc = allocator_type());//默认构造函数
explicit list (size_type n);//一般构造函数,初始化list为n个默认值
explicit list (size_type n, const value_type& val, const allocator_type& alloc = allocator_type());//一般构造函数,初始化list为n个val

template <class InputIterator>
list (InputIterator first, InputIterator last, const allocator_type& alloc = allocator_type());//一般构造函数,参数为迭代器
	
list (const list& x);//拷贝构造函数
list (initializer_list<value_type> il, const allocator_type& alloc = allocator_type());//一般构造函数,列表初始化时被调用

构造函数中的allocator_type是一个模板类,作用是提供类型化的内存分配以及对象的分配和撤销

前三个构造函数都是explicit,作用是为了防止隐式转化,具体说明见博客https://blog.csdn.net/Master_Cui/article/details/106885137

示例1

void makelist()
{
	list<int> l1(5,6);
	for (list<int>::iterator it =l1.begin();
		it!=l1.end();++it) {
		cout<<*it<<endl;
	}

	list<int> l2(++l1.begin(), --l1.end());
	for (list<int>::iterator it =l2.begin();
		it!=l2.end();++it) {
		cout<<*it<<endl;
	}

	list<int> l3={1,2,3,4};
	for (list<int>::iterator it =l3.begin();
		it!=l3.end();++it) {
		cout<<*it<<endl;
	}
}

 

注意:因为双向链表的数据结构是链式结构,不是线性结构,所以list的迭代器是双向非随机的,所以不能对list进行随机访问(下标访问),所以不能对迭代器执行以下操作

iter+n
iter-n
iter+=n
iter-=n
iter1-iter2
>, <, >=, <=

总之,list的迭代器不是随机迭代器,迭代器的比较,加减都是非法的,而且凡是用到随机迭代器的算法都不能用来处理list

如果想访问list中的元素,只能通过迭代器进行遍历,或是使用back和front函数进行访问

 

 

2.list的赋值

除了使用等号之外,和vector与string类似,也可以使用assign操作

template <class InputIterator>
void assign (InputIterator first, InputIterator last);

void assign (size_type n, const value_type& val);
void assign (initializer_list<value_type> il);

使用方式同vector与string

 

3.list的访问

由于list的数据是链式结构,所以迭代器是双向非随机的,所以list不支持下标访问和at操作,只能通过front和back函数访问以及通过迭代器进行遍历访问

front函数形式

reference front();
const_reference front() const;

back的函数形式

reference back();
const_reference back() const;

示例和vector与string类似,见博客https://blog.csdn.net/Master_Cui/article/details/107427911

使用迭代器遍历list的情况也和string与vector类似,见上面的示例1

 

4.list的交换

void swap (list& x);

示例和vector与string类似,见博客https://blog.csdn.net/Master_Cui/article/details/107427911

 

5.list的添加

因为list是双向链表,可以双向操作,所以不仅支持push_back,还支持push_front,当然也支持和vector与string类似的insert

void push_back (const value_type& val);
void push_front (const value_type& val);

iterator insert (const_iterator position, const value_type& val);
iterator insert (const_iterator position, size_type n, const value_type& val);

template <class InputIterator>
iterator insert (const_iterator position, InputIterator first, InputIterator last);
iterator insert (const_iterator position, initializer_list<value_type> il);

上述的函数都是添加元素的操作,insert的返回值依然是第一个插入元素的迭代器,和vector与string一样,参数形式也类似

 

6.list的删除

因为list是双向链表,可以双向操作,所以同时支持pop_back和pop_front,也支持erase

void pop_back();
void pop_front();

iterator erase (const_iterator position);
iterator erase (const_iterator first, const_iterator last);

两个erase函数的返回值依然是被删除元素的下一个元素的迭代器,和vector与string一样,参数形式也类似

 

删除操作除了上述四个,还有下面两个

void remove (const value_type& val);//删除所有值为val的元素

template <class Predicate>
  void remove_if (Predicate pred);//删除所有使pred(ele)为true的元素(删除所有满足条件的元素)

pred可以是一个函数指针或者函数对象或者lambda表达式

 

示例1

void removetest()
{
	list<int> l;
	l.assign(5,6);
	l.insert(l.begin(), 3,1);
	for (list<int>::iterator it = l.begin();
		it!=l.end();++it)
	{
		cout<<*it<<",";
	}
	cout<<endl;

	l.remove(6);//删除所有的6
	for (list<int>::iterator it = l.begin();
		it!=l.end();++it)
	{
		cout<<*it<<",";
	}
	cout<<endl;
}

 

示例2

bool isodd(int val) {return val%2==0;}
void removetest2()
{
	list<int> l={1,2,3,4,5,6,7};
	l.remove_if(isodd);//如果是偶数,删除,否则留下
	for (list<int>::iterator it = l.begin();
		it!=l.end();++it)
	{
		cout<<*it<<",";
	}
	cout<<endl;
}

 

7.list的迭代器失效

list的迭代器失效只有一种情况,那就是在删除erase操作后,没有更新迭代器

void iteratorfailed()
{
	list<int> l={1,2,3,4,5,6,7};
	list<int>::iterator it=++l.begin();
	l.erase(it);
	cout<<*it<<endl;
}

解决办法就是通过erase的返回值更新迭代器it

 

list迭代器失效的情况之所以很少,是因为list的数据结构导致的,因为list是双向链表,是链式结构,所以元素和元素之间只依靠指针来访问,不能通过下标访问,在删除或添加元素的时候,只需要将指针从新指向新的下一个元素即可,所以,list的删除插入操作的时间复杂度是O(1),不需要在内存中重新移动元素,也正因为元素和元素之间彼此独立,所以也没有预分配空间方法capacity,也就不存在重新分配内存的问题,基于以上原因,对list操作几乎不会使迭代器失效

 

虽然list没有capacity函数,但是常规的size,empty,resize函数都是有的,同vector与string

 

参考

《C++ Primer》

《C++标准库》

http://www.cplusplus.com/reference/list/list/

 

欢迎大家评论交流,作者水平有限,如有错误,欢迎指出

©️2020 CSDN 皮肤主题: 书香水墨 设计师:CSDN官方博客 返回首页