C++primer第五版第九章学习笔记

时间:2022-10-06 00:03:37

1.概述

顺序容器类型 描述 优点 缺点
vector 可变长数组 支持快速随机访问 在尾部之外的地方插入删除元素麻烦
deque 双向队列 支持快速随机访问 在首尾部之外的地方插入删除元素麻烦
list 双向链表 任何位置插入删除元素快 只能双向顺序访问
forward_list 单向链表 任何位置插入删除元素快 只能单向顺序访问
array 固定长度数组 支持快速随机访问 不能添加删除元素
string 字符串 支持快速随机访问 在尾部之外的地方插入删除元素麻烦
支持快速随机访问意味着数据在内存中连续存储,可使用下标随机访问,可进行快速查找(二分搜索)。
list和forward_list中的数据在内存中采用链式存储,数据之间用指针联系,优势是插入和删除元素快,但只能顺序访问,查找效率受限。

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

2.类型别名

  • iterator
  • const_iterator
  • size_type
  • difference_type
  • value_type
  • reference
  • const_reference

3.构造与初始化

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支持拷贝操作,前提是两者大小和元素类型都相同。

4.赋值和swap

c1 = c2
c = {a, b, c, ...}
swap(c1, c2)                //c1和c2必须具有相同的类型
c1.swap(c2)
//只有顺序容器(不包含array)才可以使用assign
seq.assign(b, e)            //将容器内的元素替换为迭代器范围内的值
seq.assign(n, t)
seq.assign({a, b, c, ...})

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;

5.容器大小的操作

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

添加元素

添加元素操作  描述 vector list   forward_list array  deque
push_back
emplace_back
尾部添加元素 ok ok 不支持 不支持 ok
push_front
emplace_front
首部添加元素 不支持 ok OK 不支持 ok
insert(p, t)
insert(p, n, t)
insert(p, b, e)
insert(p, {...})
在指定p位
置插入元素
ok ok 有自己
专用版本
不支持 ok
emplace 插入元素 ok ok 有自己
专用版本
不支持 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;
6.访问元素
<span style="font-size:18px;">c.front()
c.back()
c[n]
c.at(n)</span>

7.删除元素

<span style="font-size:18px;">c.pop_back()
c.pop_front()
c.erase(p)
c.erase(b,e)
c.clear()</span>

这些操作会改变容器大小,array不适用。
forward_list有特殊版本的erase。
forward_list不支持pop_back,vector不支持pop_front。
erase返回删除的(最后一个)元素之后位置的迭代器

8.改变容器的大小

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;
}

9.特殊的单向链表

单向链表有着特殊版本的插入和删除操作,原因在于单向链表自己的特性,只能对后驱元素操作,不能操作元素的前驱。

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;
}

10.string的特殊操作
  • s.substr(pos, n) pos开始n个字符的子串
  • append
  • replace
  • assign
  • s.find(args) //args可以是(c,pos) (s2,pos) (cp,pos),pos默认为0,表示从pos位置开始搜索
  • s.rfind()
  • s.find_first_of()
  • s.find_last_of()
  • s.find_first_not_of()
  • s.find_first_not_of()
11.容器适配器

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

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

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