题记:
关于容器,
C++primer
有详细描述,网上也有各种资料,而我写这篇文章的意义在于:阅读
C++primer
之后的总结、复习以及理解。
概念
容器(
container
),保存一组给定类型对象的类型。
因为引用不是对象,所以,容器的类型不能是引用。
每个标准库容器类型都是一个模板类型。为了定义容器,我们必须指定保存在容器中元素的类型。除了
array
之外,标准库容器的大小都是可变的。
顺序容器
(sequentail container)
,元素在容器的顺序,和加入容器时的位置相对象,元素通过位置来访问。
关联容器(
associative container
),元素通过关键字高效访问。
vector
,顺序容器,可变大小数组,
vector
中的元素可以通过下标访问,支持快速随机访问。在尾部插入或者删除元素效率很高,其他位置插入删除元素会导致数组重新分配空间,效率有限。在
vector
添加或者删除元素,如果导致内存重新分配,那么整个迭代器都会失效,否则,修改点之后的迭代器失效。
deque
,顺序容器,双端队列。元素可通过下标访问,支持快速随机访问,支持在头尾快速插入删除元素。
deque
在各方面都和
vector
类似,唯一的差别是,支持在容器的头尾快速删除插入元素,而且两端的插入删除操作不会导致重新分配空间。
list
,双向链表,只支持双向顺序访问。从一个给定元素开始,为了访问另一个元素,需要遍历链表。可在任何位置高效的插入删除元素而不使迭代器失效(除了删除的元素之外)。
forward_list
,单向链表。
C++11
新加入,元素只能顺序访问。其设计目标是达到于最好的手写单链表有相当的性能。因此,
forward_list
没有
size
操作。
array
,固定大小数组,创建时必须给定大小。支持快速随机访问,不能添加或者删除元素。
string
,与
vector
相似的容器,但专门用于保存字符。
在顺序容器中,除了固定大小的
array
之外,其他容器都提供高效的、灵活的内存管理。可以添加、删除、扩张或者缩小容器大小。容器保存元素的策略(内部数据结构),对容器的操作效率有固定的重大的影响。某些情况下,策略还决定是否支持容器特定操作。
例如,
vector
和
string
都是将元素保存在连续的内存空间,可以用下标快速访问,但是要在中间插入或者删除元素,就会破坏空间的连续性,导致移动修改点之后的元素或者重新分配空间来保持空间连续性,所以,插入删除效率就不高。相对的,
list
是以链表的形式保存元素的,所以,插入或者删除元素都很快,只需重新维护插入删除位置的链表即可,但是不支持随机访问,需遍历链表。而且,相对于
vector
,
list
的内存开销会很大。而
deque
是一个更为复杂的数据结构,详情可参考
http://www.cnblogs.com/zhangchaoyang/articles/2277209.html
。总之,容器的内部数据结构决定其访问能力、效率。
迭代器
迭代器是一种类型,用于访问容器中的元素或者再元素之间移动。迭代器的实现可能是指针、也可能是类,参考
http://blog.youkuaiyun.com/yxysdcl/article/details/5567460
标准容器迭代器的运算符:
iter->mem
(解引用)、
*iter
(返回元素引用)、
++
、
--
、
==
、!
=
,其中
forward_list
不支持
—
运算。除此之外,
vector
、
string
、
deque
、
array
的迭代器还支持以下运算:
+n
、
-n
、
+=n
、
-=n
、
iter1-iter2
、
>
、
<
、
<=
、
>=
。
迭代器范围:一个迭代器范围由一对迭代器表示,分别指向同一容器不同元素,为左闭合区间。
反向迭代器:按逆序寻址元素的迭代器,不支持
forward_list
。
begin()
返回容器第一个元素的迭代器;
rbegin()
返回反向迭代的第一个元素;
cbegin()
返回
const
类型的迭代器。其中不以
c
开头的
begin
和
rbegin
是重载过的,有两个版本,分别返回
iterator
和
const_iterator
类型。对应的
end()
、
rend()
、
cend()
、
crend()
类似。以
c
开头的迭代器是
C++11
新引入的,结合
auto
使用比较方便。另外,
forward_list
还有一个首前迭代器,返回第一个元素之前的迭代去,
before_begin(),cbefore_begin()
。
定义和初始化
其中,表格中第二个构造函数
C c1(c2)
,
c1
和
c2
必须有相同的类型,否则报错。其他函数满足类型的隐式转换也可以。使用大括号的第四个和第五个为列表初始化,显示的指定每个元素的值,同时,隐式的指定了容器的大小(
array
)除外。特别的
vector<string> v{10
,
“hi”}
定义
10
个值为
hi
的元素的容器,
vector<string> v{10}
定义
10
个值为空的元素的容器。
赋值和
swap
赋值运算符要求左边和右边的运算对象具有完全相同的类型。
assign
允许我们从一个不同但相容的类型赋值,或者从容器的子序列赋值。例如,可以使用
assign
讲一个
vector
中的一段
char*
值赋予一个
list
中的
string
。
添加元素
array
是固定数组。
forward_list
是单链表,故此,不支持
push_back
、
emplace_back
。同理
vector
和
string
是动态数组,不支持在头部插入数据。
insert
和
emplace
在容器的特定位置插入元素,该位置由第一个参数
p
(迭代器)指定,在
p
之前插入后续参数指定的元素,返回插入的第一个元素的迭代器。
C++11
标准引入了三个
emplace
成员,这些操作直接构造元素,例如:
访问元素
迭代器解引用、
back
、
front
、
at
、
[]
。访问成员函数返回的是引用,可以直接用来改变元素的值。但是,在使用
auto
定义变量时,会忽略引用和顶层
const
类型,需自己添加。
删除元素
特殊的 forward_list操作
ps:
1、 容器类型不能是引用,因为引用并不是类型。
2、 接受容器大小的构造函数seq(n),其类型是explicit的,不能对类型隐式转换。如
vector<string> s("ee"); //error无构造函数可以接受源类型
当使用该构造函数时,需使用元素的默认构造函数取创建元素,所以,必须保证元素有默认构造函数。如:
// 假定Nodefault是一个没有默认构造函数的类型
vector<Nodefault>v1(10, init); //ok 提供了元素的初值
vector<Nodefault>v1(10); // error 没有初值,无法初始化
3、 array不支持普通的容器构造函数,元素被默认初始化。如
array<int, 10> i; // 默认初始化为0
array<string, 2> ss = {"123" }; //第一个元素为123,后一个元素为空