顺序容器
一个容器就是一些特定类型对象的集合。顺序容器(sequencialcontainer)为程序员提供了控制元素存储和访问顺序的能力。
顺序容器类型 | ||
vector | 大小可变数组 | 支持随机访问;在尾部之外的位置插入元素比较慢。 |
string | 与vector相似的容器 | |
deque | 双端队列 | 支持随机访问;在deque的两端插入或删除元素都是很快的。 |
list | 双端链表 | 只支持双向顺序访问;在list中任何位置进行插入/删除操作速度都很快。 |
forward_list | 单向链表 | 只支持单向顺序访问;插入/删除操作速度快。 |
array | 固定大小数组 | 支持随机访问,但不能添加和删除元素。比内置数组更安全,更易于使用。 |
*现代C++程序应该使用标准库容器,而不是原始的数据结构,如内置数组。
容器操作 | |
类型别名 | |
iterator | 此容器类型的迭代器类型 |
const_iterator | 可以读取元素,但不能修改容器元素的迭代器 |
size_type | 无符号整数类型,表示容器内元素数量 |
value_type | 元素类型 |
构造函数 | |
C c; | 默认构造函数,构造空容器(array比较特殊) |
C c1(c2); | 构造c2的拷贝c1 |
C c(b, e); | 将迭代器b和e指定范围内的元素拷贝到c(array不支持) |
C c{a,b,c…}; | 列表初始化 |
赋值和swap | |
c1 = c2; | 将c1中的元素替换为c2 |
c1 = {a,b,c}; | 将c1中的元素替换为列表中的元素(不适用array) |
c1.swap(c2); | 交换a和b的元素 |
swap(c1,c2); | 交换a和b的元素 |
大小 | |
c.size(); | c中元素数目(不支持forward_list) |
c.max_size(); | c可保存的最大元素数目 |
c.empty(); | 若c中存储了元素,则返回false |
添加/删除元素 | |
c.insert(args); | 将args中的元素拷贝进c |
c.emplace(args); | 使用inits构造c中的一个元素 |
c.erase(args); | 删除args指定的元素 |
c.clear(); | 删除c中的所有元素 |
关系运算符 | |
== != | 所有容器都支持相等(不相等)运算符 |
<, >, <=, >= | 关系运算符(无序关联容器不支持) |
获取迭代器 | |
c.begin(), c.end() | 返回指向c的首元素和尾元素之后位置的迭代器 |
c.cbegin(), c.cend() | 返回const_iterator |
反向容器的额外成员 | |
reverse_iterator | 按逆向寻址元素的迭代器 |
const_reverse_iterator | 不能修改元素的迭代器 |
c.rbegin(), c.rend() | 返回指向c的尾元素和首元素之前元素的迭代器 |
c. rbegin(), c.crend() | 返回const_reverse_iterator |
*forward_list不支持size()操作,不支持逆序迭代器,forward_list迭代器不支持--运算符。
注意事项:
1. 将一个容器初始化为另一个容器
将一个容器创建为另一个容器的拷贝的方法有两种:可以直接拷贝整个容器,或者(array除外)拷贝有一个迭代器对指定的元素范围。
[1].当将一个容器初始化为另一个容器的拷贝时,两个容器的容器类型和元素类型都必须相同。
[2].当传递迭代器参数来拷贝一个范围时,就不要求容器类型是相同的了。而且,新容器和原容器的元素类型也可以不同,只要能将要拷贝的元素进行转换。
list<string>authors = { “Milton”,“Shakespeare”,“Auston”};
deque<string>authList(authors); //错误,容器类型不匹配
vector<constchar*> articles = {“a”,“an”,“the”};
forward_list<string>words(articles.begin(), articles.end()); //正确,可以将const char* 转换为string
2.容器赋值运算
容器赋值运算 | |
c1 = c2; | 将c1中的元素替换为c2中元素的拷贝 |
c = {a, b, c, …}; | 将c中的内容替换为初始化列表中元素的拷贝 |
swap(c1, c2); | 交换c1和c2中的元素,c1和c2必须具有相同的类型。swap通常比从c2向c1拷贝元素快得多。 |
c1.swap(c2); | |
assign(assign操作不适用于关联容器和array) | |
seq.assign(b,e); | 将seq中的元素替换为迭代器b和e所表示的范围中的元素。 |
seq.assign(il); | 将seq中的元素替换为列表il中的元素 |
seq.assign(n, t); | 将seq中的元素替换为n个值为t的元素 |
[1].除array外,交换两个容器内容的操作保证会很快——元素本身并未交换,swap只是交换两个容器的内部数据结构。
[2].与其他容器不同,对一个string调用swap会导致迭代器、引用和指针失效。
[3].与其他容器不同,array使用swap会真正交换容器中的元素。因此,交换两个array所需的时间与array中元素的数目成正比。
3. 关系运算符
*只有当容器的元素类型也定义了相应的比较运算符时,我们才能使用关系运算符来比较两个容器。
顺序容器操作
上面介绍的是所有容器都支持的操作,下面将介绍顺序容器所特有的操作。
1. 向容器添加元素
向容器中添加元素的操作 | |
这些操作会改变容器的大小;array不支持这些操作。 forward_list有自己版本的insert和emplace; forward_list不支持push_back和emplace_back; vector和string不支持push_front和emplace_front。 | |
c.push_back(t) c.emplace_back(args) | 在c的尾部创建一个值为t或由args创建的元素。返回void。 |
c.push_front(t) c.emplace_front(args) | 在c的头部创建一个值为t或由args创建的元素。返回void。 |
c.insert(p, t) c.emplace(p, args) | 在迭代器p指向的元素之前创建一个值为t或由args创建的元素。返回指向新添加的元素的迭代器。 |
c.insert(p,n,t) | 在迭代器p指向的元素之前插入n个值为t的元素。返回指向新添加的第一个元素的迭代器;若n为0,则返回p。 |
c.insert(p, b, e) | 将迭代器b和e指定的范围内的元素插入到迭代器p指向的元素之前。b和e不能指向c中的元素。返回指向新添加的第一个元素的迭代器;若范围为空,则返回p。 |
c.insert(p,il) | il是一个花括号包围的元素值列表。将这些给定值插入到迭代器p指向的元素之前。返回指向新添加的第一个元素的迭代器;若列表为空,返回p。 |
*向一个vector、string或deque插入元素会使所有指向容器的迭代器、引用和指针失效。 string s("maple"); auto start = s.begin(); s.insert(s.begin()+1,5,'9'); s.insert(start, '1'); cout << s << endl; 程序会报错!!! *关键概念:容器元素是拷贝 当我们用一个对象来初始化容器时,或将一个对象插入到容器中时,实际上放入到容器中的是对象值的一个拷贝,而不是对象本身。随后对容器中元素的任何改变都不会影响到原始对象。 |
*将元素插入到vector、deque、和string中的任何位置都是合法的。然而,这样做可能很耗时。
*使用insert返回值:
通过使用insert返回值,可以在容器中一个特定位置反复插入一个特定值。
list<string> lst;
auto iter = lst.begin();
while(cin >> word){
iter= lst.insert(iter, word); //等价于push_front
}
*使用emplace操作
新标准引入了三个新成员——emplace, emplace_front, emplace_back, 这些操作构造而不是拷贝元素。这些操作分别对应push_front, insert, 和push_back,允许我们将元素放置在容器头部、一个指定位置之前或容器尾部。
emplace函数在容器中直接构造元素。传递给emplace函数的参数必须与元素类型的构造函数相匹配。
2. 访问元素
包括array在内的每个顺序容器都有一个front成员函数,而除forward_list外每个成员都有一个back成员函数。这两个操作分别返回首元素和尾元素的引用。
if(!c.empty()){
//val和val2是c中第一个元素的拷贝
auto val = *c.begin(), val2 = c.front();
//val3和val4是c中最后一个元素值的拷贝
auto last = c.end();
auto val3 = *(--last); //forward_list不支持递减操作
auto val4 = c.back(); //forward_list不支持
*访问元素之前要确保容器非空。如果容器中没有元素,访问操作的结果是未定义的。
在顺序容器中访问元素的操作 | |
at和下标操作只适用于string、vector、deque和array;back不适用于forward_list。 | |
c.back() | 返回c中尾元素的引用。若c为空,函数行为未定义。 |
c.front() | 返回c中首元素的引用。若c为空,函数行为未定义。 |
c[n] | 返回c中下标为n的元素的引用,n是一个无符号整数。若n>=c.size(),则函数行为未定义。 |
c.at(n) | 返回下标为n的元素的引用。如果下标越界,则抛出out_of_range异常 |
*对一个空容器调用front和back,就像使用一个越界的下标一样,是一种严重的程序设计错误。 |
有4种操作访问容器的第一个元素哦~让我们来看看吧:No1. c.front(); No2. *c.begin(); No3. c[0]; No4.c.at(0);只不过呢,使用at操作如果失败会抛出异常!但毕竟保证元素的访问正确是程序员的责任嘛~
3. 删除元素
与添加元素的多种方式相似,(非array)容器也有多种删除元素的方式。
顺序容器的删除操作 | |
这些操作会改变容器的大小,所以不适用于array。 forward_list有特殊版本的erase。 forward_list不支持pop_back; vector和string不支持pop_front | |
c.pop_back() | 删除c中尾元素,若c为空,则函数行为未定义。函数返回void。 |
c.pop_front() | 删除c中首元素,若c为空,则函数行为未定义。函数返回void。 |
c.erase(p) | 删除迭代器p所指定的元素,返回一个指向被删元素之后元素的迭代器,若p指向尾元素,则返回尾后迭代器。若p是尾后迭代器,则函数行为未定义。 |
c.erase(b, e) | 删除迭代器b和e所指定范围的元素,返回一个指向最后一个被删元素之后元素的迭代器,若e本身就是尾后迭代器,则函数也返回尾后迭代器。 |
c.clear() | 删除c中所有元素,返回void。 |
*删除deque中除首尾元素之外的任何元素都会使所有迭代器、引用和指针失效。指向vector或string中删除点之后的迭代器、引用和指针都会失效。 *删除元素的成员函数并不检查其参数。在删除元素之前,程序员必须确保它是存在的。 |
4. 特殊的forward_list操作
在forward_list中插入或删除元素的操作 | |
lst.before_begin() lst.cbefore_begin() | 返回指向链表首元素之前不存在的元素的迭代器。此迭代器不能解引用。cbefore_begin()返回一个const_iterator |
lst.insert_after(p,t) lst.insert_after(p,b,e) lst.insert_after(p,n,t) lst.insert_after(p,il) | 在迭代器p之后的位置插入元素。t是一个对象,n是数量,b和e是表示范围的一对迭代器,il是一个花括号列表。返回指向最后一个插入元素的迭代器。若范围为空,则返回p。若p为尾后迭代器,则函数行为未定义。 |
emplace_after(p,args) | 使用args在p位置之后创建一个元素。返回一个指向这个新元素的迭代器。若p为尾后迭代器,则函数行为未定义。 |
lst.erase_after(p); lst.erase_after(b, e); | 删除p指向的位置之后的元素,或删除从b到(但不包含)e之间的元素。返回一个指向被删元素之后元素的迭代器,若不存在这样的元素,则返回尾后迭代器。如果p指向lst的尾元素或者一个尾后迭代器,则函数行为未定义。 |
5. 改变容器大小
我们可以用resize来增大或缩小容器,与往常一样,array不支持resize。如果当前大小大于所要求的大小,容器后面的元素会被删除;如果当前大小小于新大小,会将新元素添加到容器后部。
顺序容器大小 | |
resize不适用于array | |
c.resize(n); | 调整c的大小为n个元素。若n<c.size(),则多出的元素被丢弃。若必须添加新元素,对新元素进行值初始化。 |
c.resize(n,t); | 调整c的大小为n个元素。任何新添加的元素都初始化为值t。 |
如果resize缩小容器,则指向被删除元素的迭代器、引用和指针都会失效;对vector、string和deque进行resize可能导致迭代器、指针和引用失效。 |
6. 容器操作可能使迭代器失效
[1].对于list和forward_list,添加或删除元素后,指向容器的迭代器(包括尾后迭代器和首前迭代器)、引用和指针仍有效。
[2].由于向迭代器添加元素和从迭代器删除元素的代码可能会使迭代器失效,因此必须保证每次改变容器的操作之后都正确的重新定位迭代器。这个建议对vector、string和deque尤为重要。
7. 管理容量的成员函数
容器大小管理操作 | |
shrink_to_fit只适用于vector、string和deque。 capacity和reserve只适用于vector和string。 | |
c.shrink_to_fit() | 请将capacity()减少为与size()相同大小 |
c.capacity() | 不重新分配内存空间的话,c可以保存多少元素。 |
c.reserve(n) | 分配至少能容纳n个元素的内存空间 |
7. 额外的string操作
[1].构造函数
构造string的额外操作 | |
n、len2和pos2都是无符号数 | |
string s(cp, n)
string s(s2, pos2)
string s(s2, pos2, len) | s是cp指向的数组中前n个字符的拷贝,此数组至少应包含n个字符 s是s2从下标pos2开始的字符的拷贝。若pos2 > s2.size(),则函数行为未定义。 s是s2从下标pos2开始len2个字符的拷贝。若pos2>s2.size(),构造函数行为未定义。不管len2的值是多少,构造函数至多拷贝s2.size()-pos2个字符。 |
[2].substr操作
子字符串操作 | |
s.substr(pos, n) | 返回一个string,包含s中从pos开始的n个字符的拷贝。pos的默认值为0。n的默认值是s.size()-pos,即拷贝从pos开始的所有字符。 |
[3].改变string的其他方法
修改string的操作 | |||||||||||||||
s.insert(pos, args) | 在pos之前插入args指定的字符。pos可以是一个下标或一个迭代器。接收下标的版本返回一个指向s的引用;接收迭代器版本返回指向第一个插入元素的迭代器。 | ||||||||||||||
s.erase(pos, len) | 删除从位置pos开始的len个字符。如果len被省略,则删除从pos开始直至s末尾的所有字符。返回一个指向s的引用。 | ||||||||||||||
s.assign(args) | 将s中的字符替换为args指定的字符。返回一个指向s的引用。 | ||||||||||||||
s.append(args) | 将args追加到s。返回一个指向s的引用。 | ||||||||||||||
s.replace(range, args) | 删除s中范围range内的字符,替换为ards指定的字符。range或者是一个下标和一个长度,或者是一对指向s的迭代器。返回一个指向s的引用。 | ||||||||||||||
args可以是下列形式之一; append和assign可以使用所有形式。 str不能与s相同,迭代器b和e不能指向e。
|
[4].string搜索操作
string搜索操作 | |
搜索操作返回指定字符出现的下标,如果未找到则返回npos。 | |
s.find(args) | 查找s中args第一次出现的位置 |
s.rfind(args) | 查找s中args最后一次出现的位置 |
s.find_first_of(args) | 在s中查找args任一字符第一次出现的位置 |
s.find_last_of(args) | 在s中查找args任一字符最后一次出现的位置 |
s.find_first_not_of(args) | 在s中查找第一个不在args中的字符 |
s.find_last_not_of(args) | 在s中查找最后一个不在args中的字符 |
args必须是以下形式之一:
c,pos | 从s中位置pos开始查找字符c。pos默认是0 |
s2,pos | 从s中位置pos开始查找字符串s2.pos默认是0 |
cp,pos | 从s中位置pos开始查找指针cp指向的以空字符结尾的C风格字符串。pos默认是0。 |
cp,pos,n | 从s中位置pos开始查找指针cp指向的数组的前n个字符。pos和n无默认值。 |