0


【c++ primer 笔记】第9章 顺序容器

在这里插入图片描述

🎉作者简介:👓

      博主在读机器人研究生,目前研一。对计算机后端感兴趣,喜欢
     
     
      c
     
     
      +
     
     
      +
     
     
      ,
     
     
      g
     
     
      o
     
     
      ,
     
     
      p
     
     
      y
     
     
      t
     
     
      h
     
     
      o
     
     
      n
     
     
      ,
     
     
      目前熟悉
     
     
      c
     
     
      +
     
     
      +
     
     
      ,
     
     
      g
     
     
      o
     
     
      语言,数据库,网络编程,了解分布式等相关内容
     
    
   
   
    \textcolor{orange}{博主在读机器人研究生,目前研一。对计算机后端感兴趣,喜欢c++,go,python,目前熟悉c++,go语言,数据库,网络编程,了解分布式等相关内容}
   
  
 博主在读机器人研究生,目前研一。对计算机后端感兴趣,喜欢c++,go,python,目前熟悉c++,go语言,数据库,网络编程,了解分布式等相关内容

📃

      个人主页:
     
    
   
   
    \textcolor{gray}{个人主页:}
   
  
 个人主页: 小呆鸟_coding

🔎

      支持
     
     
      :
     
    
   
   
    \textcolor{gray}{支持:}
   
  
 支持:
 
  
   
    
     
      如果觉得博主的文章还不错或者您用得到的话,可以免费的关注一下博主,如果三连收藏支持就更好啦
     
    
   
   
    \textcolor{green}{如果觉得博主的文章还不错或者您用得到的话,可以免费的关注一下博主,如果三连收藏支持就更好啦}
   
  
 如果觉得博主的文章还不错或者您用得到的话,可以免费的关注一下博主,如果三连收藏支持就更好啦👍
 
  
   
    
     
      就是给予我最大的支持!
     
    
   
   
    \textcolor{green}{就是给予我最大的支持!}
   
  
 就是给予我最大的支持!🎁

💛本文摘要💛
本专栏主要是对c++ primer这本圣经的总结,以及每章的相关笔记。目前正在复习这本书。同时希望能够帮助大家一起,学完这本书。 本文主要讲解第9章 顺序容器

文章目录

❄️9.1 顺序容器概述

  • 顺序容器(sequential container):为程序员提供了控制元素存储和访问顺序的能力。这种顺序不依赖于元素的值,而是与元素加入容器时的位置相对应。
  • 顺序容器都提供了快速顺序访问元素的能力。
  • 所有容器都提供高效的动态内存管理

顺序容器类型
容器类型介绍

vector

可变大小数组。

支持快速随机访问

在尾部插入/删除速度快

deque

双端队列。

支持快速随机访问

。在头尾位置插入/删除速度很快。

list
双向链表

只支持双向顺序访问

。在

list

中任何位置进行插入/删除操作速度都很快。

forward_list
单向链表

只支持单向顺序访问

。在链表任何位置进行插入/删除操作速度都很快。

array
固定大小数组

。支持快速随机访问。不能添加或者删除元素。

string

vector

相似的容器,但专门用于保存字符。

随机访问快

。在尾部插入/删除速度快。

  • vector\deque\string\array 都是顺序存储结构。 list 是链式存储结构。但是他们都是顺序容器
  • farward_list 没有 size 操作
  • array 是一种比内置数组更好的类型。
  • list 的额外内存开销相比其他大很多。

容器的选择

  • 通常使用vector是最好的选择,除非你有很好的理由选择其他容器。
  • 如果有很多小的元素空间开销很重要,不用 list
  • 如果需要在中间位置插入/删除,用 list或forward_list
  • 如果需要在头尾位置插入/删除,用 deque
  • 如果需要随机访问,用 vector 或 deque
  • 如果需要在中间位置插入,而后随机访问: - 如果可以通过排序解决,就像插到尾部,而后排序- 在输入阶段用 list ,输入完成后拷贝到 vector 中

❄️9.2 容器库概述

'类型别名'
iterator
const_iterator
value_type           // 容器元素类型。定义方式: vector<int>::value_type
reference            // 元素类型的引用。定义方式: vector<int>::reference
const_reference      // 元素的 const 左值类型
size_type
difference_type     //带符号整形,保存俩个迭代器之间的距离'构造函数'-'三种通用的构造函数:同类拷贝、迭代器范围初始化、列表初始化'
C c1(c2);// 拷贝构造函数,容器类型与元素类型必须都相同
C c1(c2.begin(),c2.end());// 要求两种元素类型可以转换即可。
C c1{a,b,c,...};// 使用初始化列表,容器的大小与初始化列表一样大

C c(n);// 这两种接受大小参数的初始化方式只有顺序容器能用,且 string 无法使用
C c(n,t);'赋值与swap'
c1 = c2;
c1 ={a,b,c,....}
a.swap(b);swap(a, b);// 两种 swap 等价'大小'
c.size();
c.max_size();// c 可以保存的最大元素数目,是整个内存层面的容量,不是已分配内存。不同于 caplity, caplity 只能用于 vector,queue,string
c.empty();'添加/删除元素(不适用于array)'
c.insert(args);// 将 args 中的元素拷贝进 c
c.emplace(inits);// 使用 inits 构造 c 中的一个元素
c.erase(args);// 删除指定的元素
c.clear();'关系运算符'==;!=;<;<=;>;>=// 所有容器都支持相等和不等运算符。无序关联容器不支持大于小于运算符。'获取迭代器'
c.begin(); c.end(); 
c.cbegin(); c.cend();// 返回 const_iterator'反向容器的额外成员'
reverse_iterator       // 逆序迭代器,这是一个和 iterator 不同的类型
const_reverse_iterator 
c.rbegin();c.rend();
c.crbegin();c.crend();

⛄️9.2.1 迭代器

  • 迭代器范围是左闭右开,[begin(), end())
  • begin()指向容器的第一个元素end()指向容器最后元素的下一位,当begin() == end()则容器为空。
  • 所有迭代器都可以递增,forward_list 不可以递减
vector<int>::iterator iter = vec.begin();// 准确定义迭代器的方式。'c++11新特性'auto iter = vec.begin();

⛄️9.2.3 begin 和 end 成员

c.begin(); c.end();// 返回 iterator
c.cbegin(); c.cend();// 返回 const_iterator
c.rbegin();c.rend();//反向迭代器,返回reverse_iterator
c.crbegin();c.crend();//返回const_reverse_iterator

当不需要写访问时应该使用

cbegin()

cend()

⛄️9.2.4 容器定义和初始化

操作解释

C c;

默认构造函数,构造空容器

C c1(c2);

C c1 = c2;

构造

c2

的拷贝

c1
C c(b, e)

构造

c

,将迭代器

b

e

指定范围内的所有元素拷贝到

c
C c(a, b, c...)

C c = {a, b, c}

列表初始化

c
C c(n)

只支持顺序容器,且不包括

array

,包含

n

个元素,这些元素进行了值初始化

C c(n, t)

包含

n

个初始值为

t

的元素
将一个容器初始化为另一个容器的拷贝有俩种方法

  1. 直接拷贝整个容器
  2. 拷贝由一个迭代器对指定的元素范围
list<string> authors ={"hello","world","xdn"}
vector<constchar*> articles ={"a","an","the"};

list<string>list2(authors)//正确类型匹配
deque<string>de(authors)//错误,容器类型不匹配
vector<string>word(articles)//错误,元素类型不匹配
list<string>words(articles.begin(), articles.end());// 正确, const char* 类型可以转换成 string类型

注意

  • 直接拷贝整个容器,要求俩个容器的类型以及元素的类型必须匹配
  • 拷贝迭代器范围,不要求俩个容器的类型以及元素的类型必须匹配,只要元素可以进行转换就可以

array 初始化

  • 定义array既要指定元素类型,还要指定容器大小
array<int,42>;// 定义一个有42个int 的数组
array<int,42>::size_type;// 定义数组类型也需要包括元素类型和大小
  • 只有顺序容器的构造函数才接受大小参数,关联容器并不支持。
  • array具有固定大小
  • 和其他容器不同,默认构造的array是非空的
  • array 不支持普通容器的构造函数
  • array 列表初始化时,列表元素个数小于等于 array 大小,剩余元素默认初始化为 0
  • array 只能默认初始化或列表初始化,如果定义的数组很大并且需要初始化,可以先默认初始化然后用 fill 函数填充值。

array赋值

  • 不能对内置数组拷贝或赋值,但是 array 可以。
  • 使用一个 array 对另一个 array 赋值,需要两个array 元素类型与大小都相同。
  • 不能用花括号列表对 array 赋值(只可以初始化)
  • 要求俩个array的元素类型和大小都要一样
'数组'int digs[10]={0,1,2,3,4,5,6,7,8,9}int copy[10]= digs;//错误,内置数组不支持拷贝或赋值'array'
array<int,10> ar ={0,1,2,3,4,5,6,7,8,9};
array<int,10> br = ar;//正确,只要数组类型匹配即可

br ={0};//错误,只能用花括号初始化,不能赋值

⛄️9.2.5 赋值和swap

  • 下面操作适用于所有容器。除array外
  • array 类型不支持assign操作,也不允许用花括号的值列表进行赋值,只能进行初始化。
  • 对容器使用赋值运算符(除 array 外),将会使该容器的所有元素被替换。如果两个容器大小不等,赋值后都与右边容器的原大小相同。
    操作解释
    c1 = c2;
    
    c1
    
    中的元素替换成
    c2
    
    中的元素
    c1 = {a, b, c...}
    
    c1
    
    中的元素替换成列表中的元素(不适用于
    array
    
    c1.swap(c2)
    
    交换
    c1
    
    c2
    
    的元素
    swap(c1, c2)
    
    等价于
    c1.swap(c2)
    
c.assign(b, e)

c

中的元素替换成迭代器

b

e

表示范围中的元素,

b

e

不能指向

c

中的元素

c.assign(il)

c

中的元素替换成初始化列表

il

中的元素

c.assign(n, r)

c

中的元素替换为

n

个值是

t

的元素
使用assign(仅顺序容器)

  • assign 是赋值操作,可以用于顺序容器
  • “=” 要求两边类型相同, assign 要求只要可以转换即可
lst.assign(vec.begin(), vec.end());// 使用迭代器范围赋值
lst.assign(il);// il是一个花括号包围的元素值列表
lst.assign(n, t);// 将 lst 的元素替换为 n 个 t'操作等价于'
lst.clear();
lst.insert(lst.begin(), n, t);

swap

  • array ,swap 交换两个 array 中的元素值指针、引用和迭代器绑定的元素不变(值变)。
  • 对于其他容器,swap 不交换元素,只交换数据结构,因此 swap 操作非常快。
  • 对于 string,swap 后,指针、引用和迭代器会失效。对于其他容器,交换后指针指向了另一个容器的相同位置。迭代器并不失效
  • 统一使用 swap(a,b),而非 a.swap(b)
  • 对于 array,swap 操作的时间与元素数目成正比,对于其他容器,swap 操作的时间是常数。

⛄️9.2.6 容器大小操作

操作解释

c.size()
c

中元素的数目(不支持

forward_list

c.max_size()
c

中可保存的最大元素数目

c.empty()

c

中存储了元素,返回

false

,否则返回

true
  • 所有的容器都支持上面操作,只有forword_list没有size()操作

俩个容器内的元素进行比较需要保证俩点

  1. 俩个容器必须是相同类型的容器,并且保存相同类型的元素
  2. 元素类型要支持关系运算符(例如你随便定义一个类类型,如果里面没有重载关系运算符,那么你这俩个类类型,不能进行比较)

❄️9.3 顺序容器操作

⛄️9.3.1 向顺序容器添加元素

  • 注意向 vector、string 或 deque 插入元素会使所有指向容器的迭代器、引用和指针失效。
  • 添加的都是元素的拷贝,不是元素本身。
  • 头尾添加返回 void,中间添加返回指向新添加元素的迭代器
    操作解释
    c.push_back(t)
    
    c
    
    尾部创建一个值为
    t
    
    的元素,返回
    void
    
c.emplace_back(args)

同上

c.push_front(t)

c

头部创建一个值为

t

的元素,返回

void
c.emplace_front(args)

同上

c.insert(p, t)

在迭代器

p

指向的元素之前创建一个值是

t

的元素,返回指向新元素的迭代器

c.emplace(p, args)

同上

c.insert(p, n, t)

在迭代器

p

指向的元素之前插入

n

个值为

t

的元素,返回指向第一个新元素的迭代器;如果

n

是0,则返回

p
c.insert(p, b, e)

将迭代器

b

e

范围内的元素,插入到

p

指向的元素之前;如果范围为空,则返回

p
c.insert(p, il)
il

是一个花括号包围中的元素值列表,将其插入到

p

指向的元素之前;如果

il

是空,则返回

p
  • 因为这些操作会改变大小,因此不适用于array

push

  • vector 和 string 不支持 push_frontemplace_front
  • forward_list 不支持 push_backemplace_back
  • forward_list有自己专有版本的insert和emplace。
c.push_back(t);// 尾部添加一个 t
c.push_front(t);// 头部添加一个 t

insert

  • insert 返回值是指向添加的元素中第一个元素的迭代器
  • inset 是在迭代器指向的位置之前插入一个值
  • emplace 函数在容器中直接构造元素,传递给emplace函数的参数必须与元素类型的构造函数相匹配,因此一般可以为空(默认初始化)。

emplace(c++ 11)

  • 新标准引入三个新成员- emplace_front, emplace_back, emplace
  • 当调用push 或insert成员函数时,是将元素拷贝到容器中,而emplace 则将参数传递给元素类型的构造对象
  • emplace 返回值是指向添加元素的迭代器
c.emplace_back(args);// 在尾部添加一个由 args 构建的元素
c.emplace_front(args);// 在头部添加一个由 args 构建的元素
c.emplace(p,args);// 在迭代器 p 所指元素之前添加一个由 args 构建的元素

⛄️9.3.2 访问元素

  • 访问元素要确保容器非空,不然会出现错误
  • at和下标操作只适用于string、vector、deque、array。
    操作解释
    c.back()
    
    返回
    c
    
    中尾元素的引用。若
    c
    
    为空,函数行为未定义
    c.front()
    
    返回
    c
    
    中头元素的引用。若
    c
    
    为空,函数行为未定义
    c[n]
    
    返回
    c
    
    中下标是
    n
    
    的元素的引用,
    n
    
    时候一个无符号证书。若
    n>=c.size()
    
    ,则函数行为未定义
    c.at(n)
    
    返回下标为
    n
    
    的元素引用。如果下标越界,则抛出
    out_of_range
    
    异常

begin/end

  • begin()/end() 返回迭代器

front/back

  • front()/back() 返回元素的引用,可以对元素进行修改
c.front();
c.back();//返回的是尾元素的引用(注意不同于尾后迭代器)

at

  • at返回下标为 n 的元素的引用
  • at可以快速随机访问的容器都可以使用下标。
  • 使用下标一定要保证下标不越界,可以用 at 函数。当下标越界,at 函数会抛出一个 out_of_range 异常
c[n]
c.at(n);//返回下标为 n 的元素的引用

auto 获得引用

  • 如果要通过 auto 获得元素的引用,定义时一定要记得加上引用符号
c.front()=42;//将42赋予c中的第一个元素auto&v = c.back();//获取指向最后一个元素的引用
v =1024;//通过引用可以改变元素的值auto v2 = c.back();//v2不是一个引用,它是c.back()的一个拷贝
v2 =0;//未改变c中的元素

理解

  1. c.front() 返回的是引用,因此可以通过 c.front() = 32; 来给 c 的首元素赋值。
  2. auto b = c.front()得到的 b 是等号右端元素的拷贝,不是引用

⛄️9.3.3 删除元素

  • 头尾删除返回 void,特定位置删除返回被删除元素之后元素的迭代器
  • vector/string 不支持 pop_frontforward_list 不支持 pop_back。
  • forward_list 有自己特殊版本的 erase。
    操作解释
    c.pop_back()
    
    删除
    c
    
    中尾元素,若
    c
    
    为空,则函数行为未定义。函数返回
    void
    
c.pop_front()

删除

c

中首元素,若

c

为空,则函数行为未定义。函数返回

void
c.erase(p)

删除迭代器

p

指向的元素,返回一个指向被删除元素之后的元素的迭代器,若

p

本身是尾后迭代器,则函数行为未定义

c.erase(b, e)

删除迭代器

b

e

范围内的元素,返回指向最后一个被删元素之后元素的迭代器,若

e

本身就是尾后迭代器,则返回尾后迭代器

c.clear()

删除

c

中所有元素,返回

void
  • 删除迭代器所指定的元素,返回一个指向被删除元素之后元素的迭代器

删除多个元素

c.clear();
c.erase(c.begin(), c.end());// 和 c.clear() 等价

总结

  • 删除之前要确保元素存在
  • 删除 deque 种除首尾之外的任何元素都会使所有迭代器、引用和指针失效。
  • 删除 vector 或 string 中的元素会使指向删除点之后位置的迭代器、引用和指针失效。
  • 删除 list 中的元素不会影响指向其他位置的迭代器、引用、指针。

⛄️9.3.4 特殊的forward_list操作

  • forward_list单向链表,添加和删除操作都会同时改变前驱和后继结点,因此一般的添加和删除都不适用于 forward_list

  • forward_list定义了before_begin,即首前(off-the-begining)迭代器,允许我们再在首元素之前添加或删除元素。
    操作解释

    lst.before_begin()
    

    返回指向链表首元素之前不存在的元素的迭代器,此迭代器不能解引用。

    lst.cbefore_begin()
    

    同上,但是返回的是常量迭代器。

    lst.insert_after(p, t)
    

    在迭代器

    p
    

    之后插入元素。

    t
    

    是一个对象

    lst.insert_after(p, n, t)
    

    在迭代器

    p
    

    之后插入元素。

    t
    

    是一个对象,

    n
    

    是数量。若

    n
    

    是0则函数行为未定义

    lst.insert_after(p, b, e)
    

    在迭代器

    p
    

    之后插入元素。由迭代器

    b
    

    e
    

    指定范围。

    lst.insert_after(p, il)
    

    在迭代器

    p
    

    之后插入元素。由

    il
    

    指定初始化列表。

    emplace_after(p, args)
    

    使用

    args
    

    p
    

    之后的位置,创建一个元素,返回一个指向这个新元素的迭代器。若

    p
    

    为尾后迭代器,则函数行为未定义。

    lst.erase_after(p)
    

    删除

    p
    

    指向位置之后的元素,返回一个指向被删元素之后的元素的迭代器,若

    p
    

    指向

    lst
    

    的尾元素或者是一个尾后迭代器,则函数行为未定义。

    lst.erase_after(b, e)
    

    类似上面,删除对象换成从

    b
    

    e
    

    指定的范围。

  • inster_after在迭代器p之后的位置上插入元素,返回的是一个指向最后一个插入元素的迭代器

⛄️9.3.5 改变容器大小

  • resize() 用来增大或缩小容器。

  • 如果要求的大小小于当前大小尾部会被删除,如果要求的大小大于当前大小,会把新元素添加到尾部
    操作解释

    c.resize(n)
    

    调整

    c
    

    的大小为

    n
    

    个元素,若

    n<c.size()
    

    ,则多出的元素被丢弃。若必须添加新元素,对新元素进行值初始化

    c.resize(n, t)
    

    调整

    c
    

    的大小为

    n
    

    个元素,任何新添加的元素都初始化为值

    t
    
  • resize函数接收可选参数,用来初始化添加到容器中的元素,如果未提供此参数,新元素会值初始化。

  • 假如容器保存的是类类型,且resize向容器添加元素,我们必须提供初始值,或者元素类型必须提供默认构造函数

总结

  • 对于往容器插入元素(insert()),在迭代器p指向的元素之前,插入一个元素,返回指向新元素的迭代器
  • 对于往容器删除元素(erase()),删除迭代器p所指向的元素,返回一个指向删除元素之后的元素的迭代器
  • 对于forward_list插入操作(insert_after()),在迭代器p之后插入元素,返回一个指向最后一个插入元素的迭代器。
  • 对于forward_list删除操作(erase_after()), 删除迭代器p之后的元素,返回一个指向被删除元素之后元素的迭代器
  • 值初始化:对于容器而言,可以只提供元素个数而省略初始值,此时库会创建一个值初始化元素初值,并把它赋给容器中的所有元素。这个值初始化有容器当中元素类型决定(例如int,容器自动初始化为0,string容器自动初始化为空字符串)

⛄️9.3.6 容器操作可能使迭代器失效

  • 添加和删除元素都可能使指针、引用、迭代器失效。使用失效的指针、引用、迭代器是很严重的错误。
  • 在向容器添加元素后:- 如果容器是vectorstring,且存储空间被重新分配,则指向容器的迭代器、指针、引用都会失效。- 对于deque,插入到除首尾位置之外的任何位置都会导致指向容器的迭代器、指针、引用失效。如果在首尾位置添加元素,迭代器会失效,但指向存在元素的引用和指针不会失效。- 对于listforward_list,指向容器的迭代器、指针和引用依然有效。
  • 在从一个容器中删除元素后:- 对于listforward_list,指向容器其他位置的迭代器、引用和指针仍然有效。- 对于deque,如果在首尾之外的任何位置删除元素,那么指向被删除元素外其他元素的迭代器、指针、引用都会失效;如果是删除deque的尾元素,则尾后迭代器会失效,但其他不受影响;如果删除的是deque的头元素,这些也不会受影响。- 对于vectorstring,指向被删元素之前的迭代器、引用、指针仍然有效。

编写改变容器的循环程序

  • 必须保证每次改变容器后都更新迭代器。
  • insert 和 erase 都会返回迭代器,更新很容易。调用 erase 后,不需要递增迭代器,调用 insert 后,需要递增两次。

不要保存 end() 返回的迭代器

  • push、pop、首尾 emplace 操作都没有返回值,但是都会改变尾后迭代器,因此不能保存 end() 返回值。
  • for 循环判断条件中的 end() 每轮都会重新获取迭代器进行判断,因此不用担心(也因此速度会略慢,当不改变容器大小时,采用下标更快)

❄️9.4 vector对象是如何增长的

  • vectorstring在内存中是连续存储的,为了避免每添加一个元素就要重新分配一次空间,在每次获取新的内存空间时,vector和string通常会分配比新空间需求更大的内存空间。容器预留这些空间作为备用,可以保存新的元素。
  • 虽然每次重新分配需要移动所有元素,但是其扩张操作通常比list和deque快

管理容量的成员函数
操作解释

c.shrink_to_fit()

capacity()

减少到和

size()

相同大小

c.capacity()

不重新分配内存空间的话,

c

可以保存多少个元素

c.reverse(n)

分配至少能容纳

n

个元素的内存空间

  • shrink_to_fit只适用于vector、string和deque
  • capacity和reverse只适用于vector和string

注意

  • c.reserve(n) 不会减小容量,只会增大容量,当需求容量大于当前容量,才会分配内存,否则什么都不做。
  • c.resize(), 只改变容器中元素的数目,并不改变容器的容量
  • c.shrink_to_fit() 只是一个请求,实现时标准库可能会不执行。

capacity和size

  • capacity表示不分配新的内存空间前提下它最多保存多少元素
  • size是指它已经保存元素的数目。

❄️9.5 额外的string操作

  • 除了与顺序容器共同的操作之外,string提供了一些额外的操作。主要用于c风格字符串下标访问,允许用下标代替迭代器。

⛄️9.5.1 构造string的其他方法

操作解释

string s(cp, n)
s

cp

指向的数组中前

n

个字符的拷贝,此数组

string s(s2, pos2)
s

string s2

从下标

pos2

开始的字符的拷贝。若

pos2 > s2.size()

,则构造函数的行为未定义。

string s(s2, pos2, len2)
s

string s2

从下标

pos2

开始的

len2

个字符的拷贝。

  • string 的构造函数可以接受一个 string 或 const char* 参数用来指定开始位置,然后接受一个计数值用来指定范围。
  • 当用const char *创建string时,指针指向的数组必须是以空字符串结尾,拷贝操作遇到空字符串时停止。
  • 如果传递的是string对象,那么可以提供一个可选位置和一个计数值
  • 如果传递的是数组对象,那么如果传递了数组,并且加上计数值,那么数组就不需要以空字符串结尾,否则没有计数值的话,会出现未定义错误。
constchar*cp ="hello world!!!"//以空字符串结束的数组char a[]={'H','W'};//不是以空字符串结束的数组

string s1(cp);//正确,一空字符串结尾
string s2(a);//错误,不是空字符串结尾,而且也没有提供计数值
string s3(a,2);//正确

string s4(cp +6,5)//正确,从cp[6]开始拷贝,拷贝85个字符
string s5(s1,6,5)//正确,对象时string,从下标6的位置开始,拷贝5个字符   

substr

  • s.substr(pos,n) 返回 s 的一部分或全部拷贝,范围由参数指定。
    操作解释
    s.substr(pos, n)
    
    返回一个
    string
    
    ,包含
    s
    
    中从
    pos
    
    开始的
    n
    
    个字符的拷贝。
    pos
    
    的默认值是0,
    n
    
    的默认值是
    s.size() - pos
    
    ,即拷贝从
    pos
    
    开始的所有字符。
    string s2 = s1.substr(0,5);// 返回从下标 0 开始,长度为 5 的子序列
    string s2 = s1.substr(6);// 返回从下标 6 开始到最后的子序列
    

总结

  • 所有的拷贝或者substr有四种情况: 1. 括号中俩个参数(p, n),p + n没有超过string大小,分别是从下标p位置,拷贝n个字符2. 括号中俩个参数(p, n),p + n超过string大小,此时会拷贝到字符尾部3. 括号中一个参数(p),从下标p的位置,一直拷贝到最后4. 括号中一个参数,但是该参数超过了string的值,此时会抛出异常

⛄️9.5.2 改变string的其他方法

string 支持顺序容器的 assign、insert、erase 操作,此外还增加了两个额外的操作

  1. 接受下标版本的 insert 和 erase
  2. 接受 C 风格字符数组的 insert 和 assign
  3. append 和 replace 函数
    操作解释
    s.insert(pos, args)
    
    pos
    
    之前插入
    args
    
    指定的字符。
    pos
    
    可以使是下标或者迭代器。接受下标的版本返回指向
    s
    
    的引用;接受迭代器的版本返回指向第一个插入字符的迭代器。
    s.erase(pos, len)
    
    删除从
    pos
    
    开始的
    len
    
    个字符,如果
    len
    
    被省略,则删除后面所有字符,返回指向
    s
    
    的引用。
    s.assign(args)
    
    s
    
    中的字符替换成
    args
    
    指定的字符。返回一个指向
    s
    
    的引用。
    s.append(args)
    
    args
    
    指定的字符追加到
    s
    
    ,返回一个指向
    s
    
    的引用。
    s.replace(range, args)
    
    删除
    s
    
    中范围
    range
    
    中的字符,替换成
    args
    
    指定的字符。返回一个指向
    s
    
    的引用。

接受下标的 insert 和 erase

  • insert 和 erase 接受下标的版本返回的是一个指向 s 的引用(区别于迭代器版本返回指向第一个插入字符的迭代器)
  • insert 的所有版本都是第一部分参数为 pos,后面的参数为待插入的字符
  • erase 的所有版本的参数都是 pos,pos 分为 起始位置 和 终止位置/长度
s.insert(s.size(),5,'!');// 在 s 末尾(s[s.size()]之前)插入 5 个感叹号,注意实际上不存在 s[s.size()];
s.insert(0, s2,3, s2.size()-3);// 在 s[0] 之前插入 s2 第四个字符开始的 s2.size()-3 个字符
s.erase(s.size()-5,5);// 从 s 删除最后 5 个字符

接受 C 风格字符数组的 insert 和 assign

  • assign 的所有版本的参数都是要赋的值,由 起始位置 + 终止位置/长度 组成
  • replace 的所有版本的参数都是第一部分参数为要删除的范围,第二部分为要插入的字符。
constchar* cp ="stately,plump Buck";
s.assign(cp,7);// 用从 cp 开始的前 7 个字符向 s 赋值
s.insert(s.size(), cp+7);// 将从 cp+7 开始到 cp 末尾的字符插入到 s 末尾

append 和 replace

  • append:在 string 末尾进行插入操作。
  • replace:等价于调用 erase 和 insert 操作。
s.append(" 4th Ed.");// 等价于 s.insert(s.size()," 4th Ed.");

s.erase(11,3);
s.insert(11,"5th")//等价于
s.replace(11,3,"5th");// 从下标 11 开始,删除三个字符并插入 3 个新字符

⛄️9.5.3 string搜索操作

  • string类提供了6个不同的搜索函数,每个函数都有4个重载版本。
  • 每个搜索操作都返回一个string::size_type值,表示匹配发生位置的下标。如果搜索失败则返回一个名为string::npos的static成员(类型是string::size_type,初始化值是-1,也就是string最大的可能大小)。

注意

  • findrfind 查找的是给定的整个 args,而剩下的查找的是给定的 args 中包含的任意一个字符
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         // 字符,pos 为搜索开始位置(    从s中位置pos开始查找字符c。pos默认是0)
s2,pos       // 字符串(    从s中位置pos开始查找字符串s。pos默认是0)
cp,pos       // 以空字符结尾的 c 风格字符串(从s中位置pos开始查找指针cp指向的以空字符结尾的C风格字符串。pos默认是0)
cp,pos,n     // c 风格字符串的前 n 个字符(    从s中位置pos开始查找指针cp指向的前n个字符。pos和n无默认值。)

使用 pos 循环查找所有 str 包含的字符的位置

string::size_type pos =0;while((pos=s.find_first_of(str,pos))!= string::npos ){
    cout << pos << endl;++pos;}

⛄️9.5.4 compare 函数

  • 逻辑类似于C标准库的strcmp函数,根据s是等于、大于还是小于参数指定的字符串
  • 俩个string对象比较或者一个string对象一个字符数组, 可以比较整体或一部分字符串
  • s.compare返回0、正数或负数
int cmp = s.compare(s2);int cmp = s.compare(pos1,n1,s2);// 将 s 中 pos1 开始的前 n1 个字符与 s2 比较int cmp = s.compare(pos1,n1,s2,pos2,n2);// 将 s 中 pos1 开始的前 n1 个字符与 s2 中从 pos2 开始的 n2 个字符进行比较int cmp = s.compare(cp)// 将 s 与 cp 指向的字符数组比较int cmp = s.compare(pos1,n1,cp);int cmp = s.compare(pos1,n1,cp,n2);

⛄️9.5.5 数值转换

转换解释

to_string(val)

一组重载函数,返回数值

val

string

表示。

val

可以使任何算术类型。对每个浮点类型和

int

或更大的整型,都有相应版本的

to_string()

。和往常一样,小整型会被提升。

stoi(s, p, b)

返回

s

起始子串(表示整数内容)的数值,

p

s

中第一个非数值字符的下标,默认是0,

b

是转换所用的基数。返回

int
stol(s, p, b)

返回

long
stoul(s, p, b)

返回

unsigned long
stoll(s, p, b)

返回

long long
stoull(s, p, b)

返回

unsigned long long
stof(s, p)

返回

s

起始子串(表示浮点数内容)的数值,

p

s

中第一个非数值字符的下标,默认是0。返回

float
stod(s, p)

返回

double
stold(s, p)

返回

long double

例子

string s2 ="pi = 3.14";double d =stod(s2.substr(s2.find_first_of("+-.0123456789")));// 先使用查询方法找出第一个数值字符(因为+ - . 0 1 2在s2中都不存在,所以继续找下一个为3,此时3在s2中是下标为5的位置),返回5,就变成了stod(s2.substr(5)),将字符串从下标为5的位置一直到结束,转换成double类型

❄️9.6 容器适配器

  • 标准库定义了三个顺序容器适配器:stack、queue、priority_queue
  • 容器,函数,迭代器都有适配器
  • 适配器是一种机制,是某种事物的行为看起来像另一种事物。

适配器的通用操作和类型

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)//同上
  • 所有适配器要求容器具有添加和删除元素的能力,因此array不能当适配器

初始化操作

  • 默认情况下,stack 和 queue 是基于 deque 实现的, priority_queue 是在 vector 之上实现的。
  • 因此可以直接用一个 deque 来初始化 stack 和 queue。注意:是直接使用容器对象,不是使用迭代器表示的范围。
  • priority_queue 不能使用无序的 vector 初始化。
deque<int> deq;
stack<int>sta(deq);// 用 deq 初始化 sta

如果要使用其他顺序容器实现适配器,要在创建适配器时用一个顺序容器作为第二个类型参数。

stack<int, vector<int>> sta;// 定义基于 vector 实现的 stack

总结

  • stack 可以构造于 vector, list, deque 之上。
  • queue 可以构造于 list, deque 之上。
  • priority_queue 可以构造于 vector、deque 之上。

栈适配器:stack

s.pop();
s.push(item);
s.emplace(args);// 由 args 构造元素并压入栈顶
s.top();
s.size();
s.empty();swap(s, s2); s.swap(s2);

队列适配器:queue

q.pop();// 删除 queue 的首元素
q.push(item);// 在 queue 末尾插入一个元素
q.emplace(args);// 构造元素

q.front();// 返回首元素
q.back();// 返回尾元素。

q.size();
q.empty();swap(q,q2);q.swap(q2);
  • queue 为先进先出队列。
  • queue 和 priority_queue 都定义在头文件 queue 中。

优先队列:priority_queue

  • 创建 stack, queue, priority_queue 时都可以用一个顺序容器作为第二个类型参数,此外创建 priority_quque 时还可以额外传递第三个参数:一个函数对象来决定如何对 priority_queue 中的元素进行排序。

大根堆和小根堆

  • priority_queue 默认采用的是 less ,默认情况下 q.top() 是最大的元素,即大根堆。
 priority_queue <int> q;// 默认采用 vector 作为容器,采用 less<Type> 比较元素,是大根堆
 priority_queue <int, vector<int>, greater<int>> q;// 采用 greater<Type> 比较元素,生成小根堆

其他操作:

q.pop();// 删除 priority_queue 的最高优先级元素
q.push(item);// 在 priority_queue 适当的位置插入一个元素
q.emplace(args);// 构造元素
q.top();// 返回最高优先级元素
q.size();
q.empty();swap(q, q2); q.swap(q2);
  • priority_queue 为元素建立优先级。新加入的元素排在所有优先级比它低的元素之前。priority_queue 元素可以重复
  • priority_queue 不能使用无序的 vector 初始化。
标签: c++ 链表 数据结构

本文转载自: https://blog.csdn.net/weixin_45043334/article/details/125947865
版权归原作者 小呆鸟_coding 所有, 如有侵权,请联系我们删除。

“【c++ primer 笔记】第9章 顺序容器”的评论:

还没有评论