c++ primer 第五版学习笔记-第九章 顺序容器

 

 

9.1 顺序容器概述

 

1.顺序容器类型

vector          可变大小数组。支持快速随机访问。在尾部之外的位置插入或删除元素可能很慢。

deque          双端队列。支持快速随机访问。在头尾位置插入/删除速度很快。

list                双向链表。只支持双向顺序访问。在list中任何位置进行插入/删除操作速度都很快。

forward_list  单向链表。只支持单向顺序访问。在链表任何位置进行插入/删除操作速度都很快。

array            固定大小数组。支持快速随机访问。不能添加或删除元素。

string           与vector相似的容器,但专门用于保存字符。随机访问快。在尾部插入/删除速度快。

   支持快速随机访问意味着数据在内存中连续存储,可使用下标随机访问,可进行快速查找(二分搜索)。
   list和forward_list中的数据在内存中采用链式存储,数据之间用指针联系,优势是插入和删除元素快,但只能顺序访问,查找效率受限。

   array定义时需用constexpr常量表达式声明大小。

2.以下是一些选择容器的基本原则

⑴.除非你有很好的理由选择其他容器,否则应使用vector

⑵.如果你的程序有很多小的元素,且额外开销很重要,则不要使用list或forward_list

⑶.如果程序要求随机访问元素,应使用vector或deque

⑷.如果程序需要在头尾位置插入或删除元素,应使用list或forward_list

⑸.如果程序需要在头尾位置插入或删除元素,但不会在中间位置进行插入或删除操作,则使用deque

⑹.如果程序只有在读取输入时才需要再容器中间位置插入元素,随后需要随机访问元素,则

--首先,确定是否真的需要再容器中间位置添加元素。当处理输入数据时,通常可以很容易地向vector追加数据,然后再调用标准库的sort函数来重排容器中的元素,从而避免在中间位置添加元素。

--如果必须在中间位置插入元素,考虑在输入阶段使用list,一旦输入完成,将list中的内容拷贝到一个vector中

 

9.2 容器库概览

 

1.容器操作

类型别名

iterator                        此容器类型的迭代器类型

const_iterator             可以读取元素,但不能修改元素的迭代器类型

size_type                    无符号整数类型,足够保存此种容器类型最大可能容器的大小

different_type             带符号整数类型,足够保存两个迭代器之间的距离

value_type                  元素类型

reference                    元素的左值类型;与value_type含义相同,非常量引用

const_reference          元素的const左值类型

构造函数

C c;                             默认构造函数,构造空容器

C c1(c2);                     构造c2的拷贝c1

C c(b,e);                      构造c,将迭代器b和e指定范围内的元素拷贝到c(array不支持)

C c(a,b,c...);                列表初始化c

C c;                 //默认初始化为空容器
C c (c2);             //拷贝构造初始化容器,c和c2必须相同类型
C c (b, e);             //迭代器指定范围初始化容器,array不适用
C c {a, b, c, ...};  //初始化列表初始化容器
C c = {a, b, c, ...};//初始化列表初始化容器
C c (n)                 //初始化为n个元素,每个元素执行值初始化,string不适用
C c (n, t)             //初始化为n个值为t的容器
 

只有顺序容器(不包含array)的构造函数才能接受大小参数。

区别于内置数组,array支持拷贝操作,前提是两者大小和元素类型都相同。

赋值与swap

c1 = c2                        将c1中的元素替换为c2中元素

c1 = {a,b,c..}                将c1中的元素替换为列表中的元素

a.swap(b)                    交换a和b的元素

swap(a,b)                    与a.swap(b) 等价

     swap通常比拷贝速度快得多。赋值相关操作会使迭代器和指针失效,但swap操作将容器内容交换并保持迭代器和指针不失效,即指向的元素不变,但已经属于不同的容器。

     特殊的,对一个string 掉用swap会导致迭代器和指针失效。

     与其它容器不同,swap两个array会真正交换它们的元素。指针和迭代器所绑定的元素保持不变,但元素值已经与另一个array中对应的元素的值进行了交换。

     尽量使用非成员函数版的swap,例子:

    vector<int> v1(10, 1);
    vector<int> v2(5, 2);
    auto it_v1 = v1.begin();
    auto it_v2 = v2.begin();
 
    for (auto &c : v1) cout << c << " ";
    cout << endl << *it_v1 << endl;
    for (auto &c : v2) cout << c << " ";
    cout << endl << *it_v2 << endl;
 
    swap(v1, v2);
 
    for (auto &c : v1) cout << c << " ";
    cout << endl << *it_v1 << endl;
    for (auto &c : v2) cout << c << " ";
    cout << endl << *it_v2 << endl;
 
    array<int, 5> a1 = {1,1,1,1,1};
    array<int, 5> a2 = {2,2,2,2,2};
    auto it_a1 = a1.begin();
    auto it_a2 = a2.begin();
 
    for (auto &c : a1) cout << c << " ";
    cout << endl << *it_a1 << endl;
    for (auto &c : a2) cout << c << " ";
    cout << endl << *it_a2 << endl;
 
    swap(a1, a2);
 
    for (auto &c : a1) cout << c << " ";
    cout << endl << *it_a1 << endl;
    for (auto &c : a2) cout << c << " ";
    cout << endl << *it_a2 << endl;

大小,注:forward_list支持max_size和empty,但不支持size。其它每个容器都有三个有关大小的操作。

c.size()                        c中元素的数目

c.max_size()                c可保存的的最大元素数目

c.empty()                     若c中存储了元素,返回false,否则返回true

添加/删除元素(不适用于array)

注:在不同容器中,这些操作的接口都不同

c.insert(args)              将args中的元素拷贝进c

c.emplace(inits)          使用inits构造c中的一个元素

c.erase(args)              删除args指定的元素

c.clear()                      删除c中的所有元素,返回void

添加元素操作 描述vectorlist  forward_listarray deque
push_back
emplace_back
尾部添加元素okok不支持不支持ok
push_front
emplace_front
首部添加元素不支持okOK不支持ok
insert(p, t)
insert(p, n, t)
insert(p, b, e)
insert(p, {...})
在指定p位
置插入元素
okok有自己
专用版本
不支持ok
emplace插入元素okok有自己
专用版本
不支持ok

添加元素会改变容器的大小,array不支持这些操作。forward_list有自己专有版本的insert和emplace。forward_list不支持push_back和emplace_back。

  • insert返回指向第一个新加入元素的迭代器
  • insert与emplace的区别:
    insert执行拷贝操作,而emplace执行元素的构造函数,在容器中直接构造元素。
    例:
  • v.push_back("sometring");
    v.emplace_back(10,'c');
    for (auto &c : v) cout<< c << endl;
    

     

访问元素

<span style="font-size:18px;">c.front()
c.back()
c[n]
c.at(n)</span>

关系运算符  list不支持< >这些运算

==,!=                         返回指向c的首元素和尾元素之后位置的迭代器

<,<=,>,>=                  关系运算符(无序关联同期不支持)

获取迭代器:cbegin不管调用对象是什么类型,始终返回const迭代器。

                     当auto和begin一起使用时,根据调用对象的类型返回迭代器的类型。

c.begin(),c.end()        返回指向c的首元素和尾元素之后位置的迭代器

c.cbegin(),c.cend()     返回const_iterator

反向容器的额外成员

reverse_iterator              按逆序寻址元素的迭代器

const_reverse_iterator    不能修改元素的逆序迭代器

c.rbegin(),c.rend()          返回指向c的尾元素和首元素之前位置的迭代器

c.crbegin(),c.crend()       返回const_reverse_iterator

 

2.容器定义和初始化

C c                             默认构造函数。如果C是一个array,则c中元素按默认方式初始化,否则c为空

C c1(c2)                     c1初始化为c2的拷贝。c1和c2必须是相同类型。

C c1 = c2

C c{a,b,c...}                c初始化为初始化列表中元素的拷贝。列表中元素的类型必须与C的元素类型相容。对于array

C c = {a,b,c..}             类型,雷彪中元素数目必须等于或少于array的大小,任何遗漏的元素都进行值初始化。

C c(b,e)                      c初始化为迭代器b和e指定范围中的元素的拷贝。范围中

C seq(n)                     seq包含n个元素,这些元素进行了值初始化;此构造函数是explicit的

C seq(n,t)                   seq包含n个初始化为值t的元素

当定义一个array时候,除了指定元素类型,还得指定容器大小:array<int,10>;

当一个容器初始化为另一个容器的拷贝时,容器类型和元素类型都必须相同,当传递一个迭代器的参数作为拷贝范围时,不要求相同。

list<string> a;
list<string> b(a);//正确,类型相同。
deque<string> c(a);//错误,类型不同。
vector<int> d(a.begin(),a.end())//正确,类型可以不相同。

3.容器赋值运算

c1 = c2                      将c1中的元素替换为c2中元素的拷贝。c1和c2必须具有相同的类型

c = {a,b,c..}                将c1中元素替换为初始化列表中元素的拷贝

swap(c1,c2)               交换c1和c2中的元素。c1和c2必须具有相同的类型。swap通常比从c2向c1拷贝元素快得多

c1.swap(c2)

assign操作不适用于关联容器和array

seq.assign(b,e)          将seq中的元素替换为迭代器b和e所表示的范围中的元素。迭代器b和e不能指向seq中的元素

seq.assign(il)             将seq中的元素替换为初始化列表il中的元素

seq.assign(n,t)           将seq中的元素替换为n个值为t的元素

两个容器相等的充分条件是:包含相同个数的元素(size()),且对位元素的值相等。

9.3 顺序容器

 

1.向顺序容器添加元素的操作

这些操作会改变容器的大小,array不支持这些操作。

forward_list有自己专有版本的insert和emplace;

forward_list不支持push_back和emplace_back。

vector和string不支持push_front和emplace_front。

c.push_back(t)                    在c的尾部创建一个值为t或由args创建的元素,返回void

c.emplace_back(args)

c.push_front(t)                     在c的头部创建一个值为t或由args创建的元素。返回void

c.emplace_front(args)

c.insert(p,t)                          在迭代器p指向的元素之前创建一个值为t或由args创建的元素。返回指向新添加元素的迭 

c.emplace(p,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

void display(int i){cout << i << " ";}
 
int main(){
    list<int> lst(10, 42);
    for_each(lst.begin(), lst.end(), display);
    cout << endl;
    lst.resize(15);//添加5个元素,执行值初始化
    for_each(lst.begin(), lst.end(), display);
    cout << endl;
    lst.resize(25, -1);//添加10个-1
    for_each(lst.begin(), lst.end(), display);
    cout << endl;
    lst.resize(5);//将容器尺寸改变为5,删除后20个元素
    for_each(lst.begin(), lst.end(), display);
    cout << endl;
 
    return 0;
}

2.在顺序容器中访问元素的操作

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异常。

 

3.顺序容器的删除操作

这些操作会改变容器大小,所以不适用于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指向尾元素,则返回尾后(off_the_end)迭代器。若p是尾后迭代器,则函数行为未定义。

c.erase(b,e)                       删除迭代器b和e所指定范围内的元素。返回一个指向最后一个被删元素之后元素的迭代器,若e本身就是尾后迭代器,则函数也返回尾后迭代器

c.clear()                            删除c中所有元素。返回void

 

4.在forward_list中插入或删除元素的操作

lst.before_begin()               返回指向链表首元素之前不存在的元素的迭代器。此迭代器不能解引用。cbefore_begin()

lst.cbefore_begin()             返回一个const_iterator

lst.insert_after(p,t)              在迭代器p之后的位置插入元素,t是一个对象,n是数量,b和e是表示范围的

lst.insert_after(p,n,t)           一对迭代器(b和e不能指向lst内), il是一个花括号列表。返回一个指向最后一的

lst.insert_after(p,b,e)          个插入元素的迭代器。如果范围为空,则返回p。若p为尾后迭代器,则函数行为

lst.insert_after(p,il)             未定义

emplace_after(p,args)        使用args在p指定的位置之后创建一个元素。返回一个指向这个新元素的迭代器。若p为尾后迭代器,则函数行为未定义。

lst.earse_after(p)                删除p指向的位置之后的元素,或删除从b之后直到(但不包含)e之间的元素。返回一个

lst.earse_after(b,e)            指向被删元素之后元素的迭代器,若不存在这样的元素,则返回尾后迭代器。如果p指向lst的尾元素或者是一个尾后迭代器,则函数行为未定义。

int main(){
    //只能在之后插入或删除
    forward_list<int> lst;
    //向迭代器之后插入元素,返回指向插入最后的元素的迭代器
    auto it1 = lst.insert_after(lst.before_begin(), 2, 2);
    cout << *it1 << endl;
    auto it2 = lst.insert_after(lst.before_begin(), {1, 3, 9});
    cout << *it2 << endl;
    for (auto& c : lst) cout << c << " ";
    cout << endl;
    //删除迭代器之后的元素,返回指向删除元素下一位置的迭代器
    auto it3 = lst.erase_after(lst.before_begin());
    cout << *it3 << endl;
    for (auto& c : lst) cout << c << " ";
    cout << endl;
    return 0;
}

5.顺序容器大小操作

resize不适用于array

c.resize(n)                           调整c的大小为n个元素。若n<c.size(),则多出的元素被丢弃。若必须添加新元素,对新元素进行值初始化

c.resize(n,t)                         调整c的大小为n个元素。任何新添加的元素都初始化为值t。

 

6.向容器添加元素之后:

⑴.如果容器是vector或string,且存储空间被重新分配,则指向容器的迭代器、指针和引用都会失效。如果存储空间未被重新分配,指向插入位置之前的元素的迭代器、指针和引用仍有效,但指向插入位置之后元素的迭代器、指针和引用将会失效。

⑵.对于deque,插入到除首尾位置之外的任何位置都会导致迭代器、指针和引用失效。如果在首尾位置添加元素,迭代器会失效,但指向存在的元素的引用和指针不会失效。

⑶.对于list和forward_list,指向容器的迭代器(包括尾后迭代器和首前迭代器)、指针和引用仍然有效。

 

当我们删除一个元素后:

⑴.对于list和forward_list,指向容器其他位置的迭代器(包括尾后迭代器和首前迭代器)、指针和引用仍然有效。

⑵.对于deque,如果在首尾之外的任何位置删除元素,那么指向被删除元素之外其他元素的迭代器、引用或指针也会失效。如果是删除deque的尾元素,则尾后迭代器也会失效,但其他迭代器、引用和指针不受影响;如果是删除首元素,这些也不会受影响。

⑶.对于vector和string,指向被删元素之前元素的迭代器、引用和指针仍然有效。

注:当我们删除元素时,尾后迭代器总是会失效。

当我们在一个循环中,插入或删除deque.string,vector中的元素的时候,不要缓存end()迭代器,因为它一直是更新的。

9.4 vector对象是如何增长的

 

1.容器大小管理操作

shrink_to_fit只使用于vector、string和deque

capacity和reserve只适用于vector和string

c.shrink_to_fit()                     请将capacity()减少为与size()相同大小

c.capacity()                           不重新分配内存空间的话,c可以保存多少元素

c.reserve(n)                          分配至少能容纳n个元素的内存空间

 

2.当添加的数超出了vector原本分配的最大容量,vector的实现采用的策略是在每次需要分配新内存空间时将当前容量翻倍。

 

9.5 额外的string操作

 

1.构造string的其他方法

string s(cp,n)                        s是cp指向的数组中前n个字符的拷贝。此数组至少应该包含n个字符。

string s(s2,pos2)                  s是string s2从下标pos2开始的字符的拷贝。若pos2>s2.size(),构造函数的行为未定义

string s(s2,pos2,len2)          s是string s2从下标pos2开始len2个字符的拷贝。若pos2>s2.size(),构造函数的行为未定义。不管len2的值是多少,构造函数至多拷贝s2.size()-pos2个字符。

 

2.子字符串操作

s.substr(pos,n)                    返回一个string,包含s中从pos开始的n个字符的拷贝。pos的默认值为0。n的默认值为s.size()-pos,即拷贝从pos开始的所有字符。

 

3.修改string的操作

s.insert(pos,args)                在pos之前插入args指定的字符。pos可以是一个下标或一个迭代器。接受下标的版本返回一个指向s的引用;接受迭代器的版本返回指向第一个插入字符的迭代器。

s.earse(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内的字符,替换为args指定的字符。range或者是一个下标和一个长度,或者是一对指向s的迭代器。返回一个指向s的引用

 

4.string类提供了6个不同的搜索函数,每个函数都有4个重载版本。

每个搜索操作都会返回一个string::size_type值,表示匹配发生位置的下标。

如果搜索失败,则返回一个名为string::npos的static成员。

标准库将npos定义成一个const string::size_type类型,并初始化为-1。

string搜索函数返回string::size_type值,该类型时一个unsigned类型,因此我们应该尽量不要使用带符号类型来保存这些返回值

 

5.string搜索操作

s.find(args)                           查找s中args第一次出现的位置//返回一个下标,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中的字符  

 

6.字符串比较函数

s.compare(args)

args形式

s2                                          比较s和s2

pos1,n1,s2                            将s中从pos1开始的n1个字符与s2比较

pos1,n1,s2,pos2,n2              将s中从pos1开始的n1个字符与s2中从pos2开始的n2个字符进行比较

cp                                         比较s与cp指向的以空字符结尾的字符数组

pos1,n1,cp                            将s中从pos1开始的n1个字符与cp指向的以空字符结尾的字符数组进行比较

pos1,n1,cp,n2                      将s中从pos1开始的n1个字符与cp指向的地址开始的n2个字符进行比较

 

7.string和数值之间的转换

string参数中第一个非空白符必须是符号(+或-)或数字。它可以以0x或0X开头来表示十六进制数。对那些将字符串转换为浮点值的函数,string参数也可以以小数点开头,并可有包含e或E来表示指数部分。对于那些将字符串转换为整型值的函数,根据基数不同,string参数可以包含字母字符,对应大于数字9的数

如果string不能转换为一个数值,这些函数抛出一个invalid_argument异常。如果转换得到的数值无法用任何类型表示,则抛出一个out_of_range异常

to_string(val)            一组重载函数,返回数值val的string表示。val可以是任何算术类型。对每个浮点类型和int或更大的整型,都有相应版本的to_string。与往常一样,小整型会被提升。

stoi(s,p,b)                 返回s的起始子串(表示整数内容)的数值,返回值类型分别是int,long,unsigned long,long long,

stol(s,p,b)                unsigned long long。b表示转换所用的基数,默认值是10。p是size_t指针,用来保存s中第一个

stoul(s,p,b)               非数值字符下标,p默认为0,即函数不保存下标。

stoll(s,p,b) 

stoull(s,p,b) 

stof(s,p)                    返回s的起始子串(表示浮点数内容)的数值,返回值类型分别是float,double或long double。参数

stod(s,p)                   p的作用于整数转换函数中一样

stold(s,p)

 

9.6 容器适配器

 

  • 默认情况下,stack和queue是基于deque实现的,priority_queue是在vector之上实现的。

  • 允许创建一个适配器时,将一个命名的顺序容器作为第二个类型参数,来重载默认容器类型。stack<string, vector<string> > str_stk;

  • 对于给定的适配器,可以使用哪些容器是受限制的。所有适配器都要求具有添加和删除元素的能力,所以不能构造在array。stack只要求push_back pop_backback操作,所以可以用除array和forward_list外任何容器类型构造。

 

1.所有容器适配器都支持的操作和类型

size_type                      一种类型,足以保存当前类型的最大对象的大小

value_type                   元素类型

container_type             实现适配器的底层容器类型

A a;                              创建一个名为a的空适配器

A a(c);                          创建一个名为a的适配器,带有容器c的一个拷贝

关系运算符                  每个适配器都支持所有关系运算符:==,!=,<,<=,>,>=,这些运算符返回底层容器的比较结果

a.empty()                     若a包含任何元素,返回false,否则返回true

a.size()                        返回a中元素数目

swap(a,b)                    交换a和b的内容,a和b必须有相同类型,包括底层容器类型也必须相同

a.swap(b)

 

2.栈的其他操作

s.pop()                         删除栈顶元素,但不返回该元素值

s.push(item)                创建一个新元素压入栈顶,该元素通过拷贝或移动item而来,或者由args构造

s.emplace(args)

s.top()                          返回栈顶元素,但不将元素弹出栈

 

3.queue和priority_queue的其他操作

q.pop()                         返回queue的首元素或priority_queue的最高优先级元素,但不删除此元素

q.front()                        返回首元素,但不删除此元素

q.back()                        返回尾元素,但不删除此元素,只适用于queue

q.top()                           返回优先级最高的元素,但不删除此元素,只适用于priority_queue

q.push(item)                 在queue末尾或priority_queue中器当的位置创建一个元素,其值为item,或者有args构造

q.emplace(args)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值