● 元素在顺序容器中的顺序与其加入容器时的位置相对应。
关联容器中元素的位置由元素相关联的关键字值决定。
● 在 deque两端添加或删除元素与list或forward_list添加删除的速度相当。
● 与内置数组相比,array
是一种更加安全、更容易使用的数组类型,array
对象的大小是固定的,array
不支持添加删除元素以及改变容器大小的操作。
● 注意: forward_list
没有 size 操作,因为保存或计算其大小就会比手写链表多出额外的开销。forward_list
还不支持“反向容器的成员”。 forward_list
迭代器不支持递减运算符
● forward_list
和 list
不支持 迭代器支持的算术运算, 只有“string、 vector 、deque 、array、”这些顺序容器类型能够使用, 我们不能将它们用于其他任何容器类型的迭代器。
注意: forward_list
支持empty、max_size
,但不支持size。
● forward_list
不支持 push_back、emplace_back、 pop_back、insert、emplace、erase
操作。
● vector 和 string
不支持 push_front、 和 emplace_front、 pop_front
.
● shrink_to_fit()函数
只适用于 vector 和 string
。
vector <string> svec;
svec.insert(svec.begin(),"Hello!"); //我们可以使用insert 。将元素插入到第一个元素之前,但是可能很耗时。
● 除了array
和forward_list
之外, 每个顺序容器(包括string类型
)都支持push_back
。
● 向一个vector、string 或 deque
插入元素会使所有指向容器的迭代器、引用和指针失效。
● 向一个vector
或string
添加元素可能引起整个对象存储空间的重新分配。 重新分配一个对象的存储空间需要分配新的内存, 并将元素从旧的空间移动到新的空间中。
● list、forward_list、deque
这些容器还支持名为push_front
的类似操作。
● vector 、list、deque、string
都支持insert
成员,forward_list
支持特殊版本的insert
成员。
● 包括array
在内的每个顺序容器都有一个front
成员函数, 而除了forward_list
之外的所有顺序容器都有一个back
成员函数。 分别返回首元素和尾元素的引用。
● array
不支持resize
操作来改变容器大小。
确定使用哪种顺序容器
● 注意 : 容器均定义模板类,我们必须提供额外的信息来生成特定的容器类型。 对大多数,但不是所有容器,还需要提供额外的元素类型信息
迭代器范围
● 一个迭代器范围由一对迭代器表示, 两个迭代器分别指向同一个容器中的元素或者是尾元素之后的位置。 通常被称为begin
和 end
, 它们标记了容器中元素的一个范围。
● 注意: begin
和 end
必须指向相同的容器。 end
可以与begin
指向相同的位置, 但不能指向begin
之前的位置
容器类型成员
● 通过类型别名, 我们可以在不了解容器中元素类型的情况下使用它。 如果需要元素类型, 可以使用容器的value_type
。 如果需要元素类型的一个引用,可以使用reference
或 const_reference
begin 和 end 成员
● 注意: 与const
指针和引用类似,可以将一个普通的iterator
转换为对应的 const_iterator
, 但反之不行。
容器定义和初始化
● 每个容器类型都定义了一个默认构造函数。 除array
之外, 其他容器的默认构造函数都会创建一个指定类型的空容器, 且都可以接受指定容器大小和元素初始值的参数。
将一个容器初始化为另一个容器的拷贝
● 将一个新容器创建为另一个容器的拷贝的方法有两种:
可以直接拷贝整个容器 //两个容器类型和元素类型必须匹配
//不要求两个容器类型和元素类型必须匹配,只要能将要拷贝的元素转换为要初始化的容器的元素类型即可。
或者(除array外)拷贝由一个迭代器对指定的元素范围进行拷贝。
接受两个迭代器参数的构造函数用这两个迭代器表示我们想要拷贝的一个元素范围 : 新容器的大小与范围中元素的数目相同。 新容器中的每个元素都用范围中对应元素的值进行初始化.
● 由于两个迭代器表示一个范围, 因此可以使用这种构造函数来拷贝一个容器中的子序列:
vector<string> authors = { "Milton","Shakespeare","Austen" };
auto it = authors.begin() + 2;// it表示authors中的一个元素
deque<string> authVector(authors.begin(), it); //拷贝元素,直到但不包括it指向的元素
● 在C++11新标准中允许我们对一个容器进行列表初始化, 除了array
之外的容器类型,初始化列表隐含地指定容器的大小: 容器将包含与初始值一样多的元素个数。
与顺序容器大小相关的构造函数
● 如果元素类型是内置类型或者是具有默认构造函数的类类型,可以只为构造函数提供一个容器大小参数。
如果元素类型没有默认构造函数,除了大小参数外, 还必须指定一个显式的元素初始值。
● 注意: 只有顺序容器的构造函数才接受大小参数,关联容器并不支持。
标准库 array 具有固定大小
● 与内置数组一样,标准库array
的大小也是类型的一部分。 当定义一个array
时,必须指定元素类型和容器大小。
● 由于大小是array
类型的一部分,array
不支持普通的容器构造函数。
● 与其他容器不同的是, 一个默认构造的array
是非空的: 它包含了与其大小一样多的元素。 这些元素都被默认初始化。
● 如果我们对array
进行列表初始化, 初始值的数目必须等于或小于array
的大小。
如果初始值数目小于array
的大小,则它们被用来初始化array
靠前的元素,所有剩余元素都会进行值初始化。
在这两种情况下,如果元素类型是一个类类型, 那么该类必须有一个默认构造函数, 以使值初始化能够进行。
● 注意: 虽然我们不能对内置数组进行拷贝或者对象赋值操作,但array
并无此限制
int digs[10] = { 0,1,2,3,4,5,6,7,8,9 };
int cpy[10] = digs; //错误
array<int,10>digits = { 0 };
array<int, 10> copy = digits; //正确,只要数组类型匹配即合法
digs = digits ;// 替换digs 中的元素
digits = { 0 }; // 错误:不能将一个花括号列表赋予数组
● 由于右边运算对象的大小可能与左边运算对象的大小不同, 因此array
类型不支持assign
, 也不允许用花括号包围的值列表进行赋值。
赋值与swap
● 注意: 如果两个容器原来大小不同,赋值运算后两者的大小都与右边容器的原大小相同。
使用 assign(关联容器和array除外)
● assign
成员: 允许我们从一个不同但相容的类型赋值,或者从容器的一个子序列赋值。assign
操作用参数所指定的元素替换(拷贝)左边容器中的所有元素。 assign
的参数决定了容器中将有多少个元素以及它们的值都是什么。
● 注意 : 由于旧元素被替换,因此传递给assign
的迭代器不能指向调用assign
的容器
使用 swap
● 注意: 除array
外,交换两个容器中的内容的操作保证会很快——元素本身并未交换,swap
只是交换了两个容器的内部数据结构。
● 注意: 除array
外, swap
不对任何元素进行拷贝、删除或插入操作,因此可以保证在常数时间内完成。
● 元素不会被移动的事实意味着, 指向容器的迭代器、引用和指针在swap
之后,都不会失效。 它们仍指向swap
操作之前所指向的那些元素。
但是,在swap
之后,这些元素已经属于不同的容器了。
注意: 与其它容器不同的是,对一个string
调用swap
会导致迭代器、引用和指针失效。
● 注意: 与其它容器不同, swap
两个array
会真正交换它们的元素。 交换两个array
所需的时间与array
中元素的数目成正比。
对于array
,在swap
操作之后 ,指针、引用和迭代器所绑定的元素保持不变, 但元素值已经与另一个array
中对应元素的值进行了交换。
● 每个容器类型都有三个与大小相关的操作: size、empty、max_size。
注意: forward_list
支持empty、max_size
,但不支持size。
关系运算符
● 每个容器类型都支持相等运算符( == 和 != ); 除了无序关联容器外的所有容器都支持关系运算符——关系运算符左右两边的运算对象必须是容器类型相同和元素类型相同。
● 比较两个容器实际上是进行元素的逐对比较。
容器的关系运算符使用元素的关系运算符完成比较
● 注意: 只有当其元素类型也定义了相应的比较运算符时,我们才可以使用关系运算符比较两个容器。
● 容器的相等运算符是使用元素的“ == ”运算符实现比较的, 而其他关系运算符是使用元素的“ < ” 运算符实现比较的。
如果元素类型不支持所需运算符, 那么保存这种元素的容器就不能使用相应的关系运算符。
向顺序容器添加元素
● 注意: 除array
外, 所有标准库容器都提供灵活的内存管理。在运行时可以动态添加或删除元素来改变容器大小。
使用push_back
在容器中的特定位置添加元素(insert)
● 每个insert
函数的第一个参数都接受一个迭代器。 该迭代器指出了在什么位置放置新元素。 它可以指向容器中的任何位置,包括容器尾部之后的下一个位置。
插入范围内元素
list <string> slist;
//运行时错误:迭代器表示要拷贝的范围,不能指向与目的位置相同的容器
slist.insert(slist.begin(),slist.begin(),slist.end());
使用 insert 的返回值
● 通过使用insert
的返回值, 可以在容器中一个特定的位置反复插入元素。
list <string> lst;
auto iter=lst.begin();
while(cin>>word)
{
iter=lst.insert(iter,word); //等价于调用 push_front
}
使用 emplace 操作
● 当我们调用一个emplace
成员函数时,则是将参数传递给元素类型的构造函数。 emplace
成员使用这些参数在容器管理的内存空间中直接构造元素。
而调用push_back 、insert push_front、
这样的操作时,则会创建一个局部临时对象,并将其压入容器中。
● 注意: emplace
函数的参数根据元素类型而变化 , 参数类型与元素类型的构造函数相匹配。 emplace
函数实在容器中直接构造(创建)元素。
访问元素
● 如果容器中没有元素,访问操作的结果是未定义的。
● 以上在容器中访问元素的成员函数返回的都是引用。
● 如果我们使用auto
变量来保存这些函数的返回值, 并且希望使用此变量来改变元素的值, 必须记得将变量定义为引用类型。
● 下标运算符接受一个下标参数,返回容器中该位置的元素的引用。
● 如果我们希望确保下标是合法的,可以使用at
成员函数。 at
成员函数类似下标运算符, 但如果下标越界, at
会抛出一个out_of_range
异常。
● 注意: 访问元素的成员函数之前, 必须确保该位置非空, 如果容器为空, 该操作的行为将是未定义的。
删除元素
● 注意: 删除元素的成员函数并不检查其参数。在删除元素之前,程序员必须确保它们是存在的。
● 注意: 如果你需要删除元素的值,就必须在执行删除操作之前保存它。
改变容器大小
● 如果当前大小大于所要求的新大小, 容器后部的元素会被删除; 如果当前大小小于新大小,会将新元素添加到容器后部。
● resize
操作接受一个可选的元素值参数, 用来初始化添加到容器中的元素。 如果调用者未提供此参数, 新元素进行值初始化。
● 如果容器保存的是类类型元素, 且resize
向容器添加新元素, 则我们必须提供初始值, 或者元素类型必须提供一个默认的构造函数。
编写改变容器的循环程序
● 注意: 添加删除vector、string、或者deque
元素的循环程序必须考虑迭代器、引用和指针可能失效的问题。 程序必须保证每个循环中都更新迭代器、引用或指针。
如果循环中调用的是insert
或erase
, 那么更新迭代器很容易, 因为这些操作都返回迭代器。
不要保存end 返回的迭代器
● 注意:当我们添加删除vector
或string
的元素后,或在deque
中首元素之外的任何位置添加删除元素后, 原来end
返回的迭代器总是会失效。
● 注意:如果在一个循环程序中插入删除deque、vector、string
中的元素, 不要缓存end
返回的迭代器,必须在每次插入删除操作后重新调用end()
,**而不能在循环之前保存end
返回的迭代器,一直当作容器末尾使用。
Vector 对象是如何增长的
● 当不得不获取新的内存空间时,vector
和string
的实现通常会分配比新的空间需求更大的内存空间。容器预留这些空间作为备用, 可以用来保存更多的新元素。这样,就不需要每次添加新元素都重新分配容器的内存空间了。
● vector
在每次重新分配内存空间时都要移动所有元素——将已有元素从旧位置移动到新的空间中,然后添加新元素,释放旧存储空间。
● 注意: reserve
并不改变容器中元素的数量, 它仅影响vector
预先分配多大的内存空间。
● 只有当需要的内存空间超过当前容量时,reserve
调用才会改变vector
容量。
如果需求大小大于当前容量,reserve
至少分配与需求一样大的内存空间(可能更大)。
注意: 如果需求大小小于或等于当前容量,reverse
什么也不做。 当需求大小小于当前容量时,容器不会退回内存空间。 因此, 在调用reverse
之后, capacity
将会大于或等于传递给reverse
的参数。
● 注意: 调用reserve
永远不会减少容器占用的内存空间。 resize
成员函数只改变容器中元素的数目,而不是容器的容量。 不能使用resize
来减少容器预留的内存空间。
● 注意: 调用shrink_to_fit
来要求deque、vector 或 string
返回不需要的内存空间。 此函数指出我们不再需要任何多余的内存空间。 但是可能调用shrink_to_fit
也并不保证一定退回内存空间。
capacity 和 size
● 注意: 只要没有操作需求超出vector
的容量, vector就不能重新分配内存空间。
● 注意: vector
实现采用的策略似乎是在每次需要分配新内存空间时将当前容量翻倍。
● 只有在执行insert
操作时 size 与 capacity
相等, 或者调用 resize
或 reserve
时给定的大小超过当前capacity
时, vector
才可能重新分配内存空间。 会分配多少超过给定容量的额外空间,取决于具体实现。