本文介绍vector和list的用法、适合的场景以及在无法估算实际业务场景中vector和list的代价情况下如何使用vector和list.
1 用法
vector和list是C++标准库提供的两种容器。其实,这两种容器与我们学过的数据结构中的数组和链表是一样的。只不过,标准库将其实现成模板让其更加的通用。下面简要介绍下C++11新增的几种方法。其他内容见张老师的课件。
1.1 构造函数
C++11支持列表初始化,因此在容器的构造函数中新增了带列表初始化的构造函数。函数声明如下:
C c{a, b, c}
有了这个构造函数,我们就可以使用如下的方式构造vector/list:
vector<int> ivec{1, 2, 3, 4, 5};
list<int> ilst{5, 6, 7, 8, 9};
在C++11以前,我们要使用下面的方式来达到相同的目的:
vector<int> oivec;
for (int i = 0;i < 5;i++)
oivec.push_back(i);
//或者
int intArray[] = {1, 2, 3, 4, 5};
vector<int> oivec1(intArray, intArray + 5);
1.2 赋值
得益于列表初始化,复制也支持列表的赋值。声明为c1 = {e1, e2, e2}
。用法与构造函数类似。
1.3 添加/删除
插入/删除新增的接口如下:
c.emplace(inits) //使用inits中的元素构造一个c元素的对象,并插入到c中
c.emplace_back(inits)
c.emplace_front(inits)
一般的,我们插入元素会使用c.insert(args)
。其中args是c中元素的对象。当调用insert的成员函数时,我们将元素类型的对象传递给容器,这些容器被拷贝到容器中。当我们使用emplace时,我们将参数传递给元素的构造函数。emplace直接使用这些元素构造对象并保存在容器中。使用示例如下:
vector<complex<int> > cvec; /*定义一个存储complex对象的数组*/
complex<int> c(1, 2);
cvec.insert(cvec.begin(), c); /*C++11之前使用的调用方式*/
cvec.emplace(cvec.begin(), 2, 3); /*c++11新增的调用方式,直接传元素的参数给emplace,emplace在调用complex来构造对象*/
2 适合场景
本节介绍vector&list的使用场景。一般vector会实现成数组的方式,即分配一段连续的内存空间在存放数组元素,而list会实现成双向链表,便于双向访问。因此,vector和list也分别具有数组和链表的优缺点。
vector的优缺点:
1.优点
-提供高效、灵活的内存管理;
-支持随机访问,访问速度非常快
2.缺点
+在数组中间插入元素需要移动后续的元素,因此插入数组很慢
+插入数据时可能伴随大量内存分配
list的优缺点:
1.优点
+list一般实现为链表,节点与节点之间是通过指针链接的,因此在list中插入/删除元素很快,只需要常量的时间。
2.缺点
+不支持随机访问,访问list内元素的速度与list的元素数量正相关。
从上面的介绍中,不难发现vector和list各自适合的场景。如果我们的业务中需要快速、随机的访问容器中的元素并且对向数组中插入数据的需求不高的话,那vector是很好的选择;如果业务对快速插入的需求高,对随机访问元素的要求低的话,那就可以选择list。当然,除此之外,还有些基本的原则:
1.通常建议使用vector,除非有很好的理由。
2.如果程序元素小且多,空间的额外开销很重要的话,不要使用list。
3 业务场景不明晰是如何使用vector和list
在实际开发过程中,可能无法正确评估vector/list对系统性能的影响,但是有需要vector和list的一些公有操作,如果插入,查询等。这时候可以定义一个适配器ListAdpter,该适配器组合vecotr/list并定义一套vector/list的公有接口。然后,我们的程序通过调用ListAdpter中的操作,进而对程序屏蔽底层的实现。然后,在对系统进行压力测试,评估出vector/list对系统的影响,而且抉择出启用哪种容器。
4 参考
1.cpp primer 5th edition
网友评论