ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

STL-顺序容器及适配器

2020-03-03 17:55:48  阅读:136  来源: 互联网

标签:容器 args string 迭代 STL 适配器 元素 array


序容器为程序员提供了控制元素存储和访问顺序的能力。这种顺序不依赖于元素值,而是与元素加入容器时的位置相对应。

9.1 概述

所有顺序容器都提供了快速顺序访问元素的能力。但是,这些容器在以下方面都有不同的性能折中:

-向容器添加或从容器删除元素的代价;

-非顺序访问容器中元素的代价。

 

顺序容器类型:

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

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

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

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

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

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

 

array固定大小;vector和string在中部插入/删除需要调整元素位置保持连续性;list和forward_list元素散布,依靠指针维持前后顺序,内存开销较大;deque支持快速随机访问,但支持头尾两端快速插入/删除,中部插入/删除也较慢(其结构较复杂)。

forward_list和array是C++11新增加。array与内置数组相比,空间是在堆上分配。forward没有size操作,其设计用来达到与最好的手写单向链表性能接近,没有多余操作。

 

对顺序容器使用时使需求和容器性能相适应,还可以使用多个容器配合使用,容器之间内容的复制很方便。

 

9.2 容器库概览

容器类型上的操作形成了一种层次:

-某些操作是所有容器类型都提供的。

-另外一些操作仅针对顺序容器、关联容器或无序容器。

-还有一些操作只适用于一小部分容器。

 

容器操作:(常用的)

类型别名

iterator  迭代器类型

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

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

value_type  元素类型

reference  元素的左值类型;与value_type&含义相同

const_reference  元素的const左值类型,const value_type&

构造函数

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

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

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

C c{a,b,c...};  列表初始化;

赋值与swap

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

c1={a,b,c...}  将c1中的元素替换为列表中元素(不适用于array)

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

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

大小

c.size()  c中元素的数目(不支持forward_list)

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

关系运算符

==,!=  所有容器都支持相等/不等运算符

<, <=, > ,>=  关系运算符(无序容器不支持)

获取迭代器

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

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

反向容器的额外成员(不支持forward_list)

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

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

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

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

 

9.2.1迭代器

标准迭代器的运算符:*it.smt ,it->smt,  ++, --, == ,!=

forward_list不支持迭代器的--,单向链表(单向指针)

 

一个迭代器范围由一堆迭代器表示,两个迭代器分别指向同一个容器中的元素或者是尾元素之后的位置。左闭右开区间。

 

9.2.4 容器定义和初始化

只有顺序容器的构造函数才能接受大小参数。

C c;  C c1(c2);  C c1=c2;  C c{a,b,c,...};  C c={a,b,c...};  C c(b,e);迭代器区间内容复制;  

顺序容器特有的构造函数: C seq(n);默认初始化;  C seq(n,t);n个元素,每个元素初始化为t。

 

标准库array具有固定大小:当定义一个array时,必须指定其大小

array<int,42>  //类型为:保存42个int的数组

array<string,10>  //保存10个string的数组

与其它容器不同,一个默认构造的array是非空的:它包含了于其大小一样多的元素。这些元素都被默认初始化,就像一个内置数组那样。

如果我们对array进行列表初始化,初始值的数目必须等于或小于array的大小。如果初始值数目小于array的大小,则它们被用来初始化array中靠前的元素,所有剩余元素都会进行值初始化。在这两种情况下,如果元素类型是一个类类型,那么该类必须有一个默认构造函数,以使值初始化能够进行。

 

 

9.2.5 赋值和swap

容器赋值运算:

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

c1={a,b,c...};  将c1中元素替换为列表中元素的拷贝(array不适用,但array构造时可用列表形式构造,列表元素数目小于等于array的大小)。

swap(c1,c2);c1.swap(c2);  交换c1和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的元素。

 

赋值运算符要求左边和右边的运算对象具有相同类型。顺序容器(除array外)还定义了一个名为assign的成员,允许我们从一个不同但相容的类型赋值,或者从容器的一个子序列赋值。

swap交换两个相同类型容器的内容。除array外,swap不对任何元素进行拷贝、删除或插入操作,因此可以保证在常数时间内完成。

元素不会被移动意味着,除了string外,指向容器的迭代器、引用和指针在swap操作后都不会失效。而对一个string调用swap会导致迭代器、引用和指针失效。

而swap两个array会真正交换它们的元素。因此,交换两个array的时间与元素数目成正比。

在新标准中,容器即提供成员函数版本的swap,也提供非成员版本的swap。

 

9.3 顺序容器操作

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.emplace_back(inits);

c.push_front(t);

c.emplace_front(inits);

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

c.insert(p,n,t);  在迭代器p指向的元素之前插入n个值为t的元素,返回指向新添加的第一个元素的迭代器。若n为0,则返回p。

c.insert(p,b,e);  在迭代器p指向的元素之前插入由迭代器b和e指向的区间,返回指向新添加的第一个元素的迭代器。b和e不能是p所在容器的迭代器。

c.insert(p,il);  il是一个花括号包围的元素值列表。将这些给定值插入到迭代器p指向的元素之前。返回指向新添加的第一个元素的迭代器;若列表为空,则返回p。

 

向一个vector、string和deque插入元素会使所有指向容器的迭代器、引用和指针失效。

 

在一个vector或string的尾部之外的任何位置,或是一个deque的首尾之外的任何位置添加元素,都需要移动元素。而且,向一个vector或string添加元素可能引起整个对象存储空间的重新分配。

 

emplace系列函数是将参数传递给元素类型的构造函数,emplace成员使用这些参数在容器管理的内存空间中直接构造函数。

而push_back、push_front、insert会创建一个临时对象,并将对象压入要添加的容器中。

 

9.3.2 访问元素

如果容器中没有元素,访问操作的结果是未定义的。

包括array在内的每个顺序容器都有一个front成员函数,而除了forward_list之外所有顺序容器都有一个back成员函数。这两个函数分别返回首元素和尾元素的引用。

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

at和下标操作只适用于string、vector、deque和array。

back不适用于forward_list。

c.back()

c.front();

c[n];  //n越界时函数行为未定义

c.at(n);  //如果下标越界,则抛出一out_of_range异常。

访问成员函数返回的是引用。

 

9.3.3 删除元素

非array容器有很多删除元素的方式。

顺序容器的删除操作:

这些操作会改变容器的大小,所以array不使用。

forward_list有特殊版本的erase,forward_list不支持pop_back;vector和string不支持pop_front。

c.pop_back();

c.pop_front();

c.erase(p);  删除p所指的元素,返回一个指向被删元素之后元素的迭代器。

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

c.clear();

删除deque中除首尾位置之外的任何元素都会使所有迭代器、引用和指针失效。指向vector或string中删除点之后位置的迭代器、引用和指针都会失效。

 

删除元素之前,必须由程序员来检查元素是否存在。

 

 

9.3.4 特殊的forward_list操作

forward_list中插入或删除元素的操作:

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

lst.cbefore_begin()

lst.insert_after(p,t) lst.insert_after(p,n,t) lst.insert_after(p,b,e) lst.insert_after(p,il)  在迭代器p之后的位置插入元素。t是一个对象,n是数量,b和e是表示一个范围的迭代器(b和e不能是在lst内),il是一个花括号列表。返回一个指向最后一个插入元素的迭代器。如果范围为空,则返回p。若p为尾后迭代器,则函数未定义。

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

lst.erase_after(p) lst.erase_after(b,e)  删除p指向的位置之后的元素,或删除从b之后直到(但不包含)e之间的元素。返回一个指向被删元素之后元素的迭代器,若不存在这样的元素,则返回尾后迭代器。如果p指向lst的尾元素或者是一个尾后迭代器,则函数行为未定义。

 

9.3.5 改变容器大小

顺序容器大小操作:

resize不适用于array

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

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

如果resize缩小容器,则指向被删除元素的迭代器、引用和指针都会失效;对vector、string或deque进行resize可能会导致迭代器、指针和引用失效。

 

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

在向容器添加元素后:

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

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

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

 

当我们删除一个元素后:

-对于list和forward_list,指向容器其它位置的迭代器(包括尾后迭代器和直前迭代器)、引用和指针仍有效。

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

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

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

 

 

9.4 vector对象增长

vector将元素连续地存储。分配的内存空间在某时是一定的,但容器大小可以改变。当容器插入元素而空间不足时,需要为vector或string重新分配空间且新空间比原空间大得多,多出来这部分作为备用(防止每次插入都要重新分配)。

容器大小管理操作:

shrink_to_fit只适用于vector、string和deque。

capacity和reverse只适用于vector和string。

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

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

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

只有当需要的内存空间超过当前容量时,reserve才会改变vector的容量。reserve需要小于当前容量时,什么也不做。

调用shrink_to_fit来要求deque、vector或string退回不需要的内存空间。

 

vector内存变长分配时,先在堆中找到一块够大的空间(原空间的1.5或2倍),将原空间的内容移过去,并将在栈中的vector变量指向这个新分配的空间。

 

9.5 额外的string操作

9.5.1 构造string的其它方法

string除了与其它顺序容器相同的构造函数外,还有另外3个构造函数:

n、len2和pos2都是无符号值。

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

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

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

 

substr操作

substr操作返回一个string,它是原始string的一部分或全部的拷贝,可以传递给substr一个可选的开始位置和计数值:

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

 

9.5.2 改变string的其它方法

string类型支持顺序容器的赋值运算符以及assign、insert和erase操作。

除了接收迭代器的insert和erase版本外,string还提供了接受下标的版本,下标指出开始删除的位置,或是insert到给定值之前的位置。

标准库string类型还提供了接受C风格字符数组的insert和assign版本。const char *cp="cjjjjj";s.assign(cp,7);

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

例如:str.append("cjj"); str.replace(11,3,"5th");

 

9.5.3 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无默认值。

 

9.5.4 compare函数

标准库string类型提供了一组compare成员函数。

compare有6个版本:3个与string比较,3个与C风格字符串比较

s.compare(s2)  比较s和s2

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

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

s.compare(cp)

s.compare(pos1,n1,cp)

s.compare(pos1,n1,cp,n2)

 

9.5.5 数值转换

string和数值之间的转换

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

stoi(s,p,b) stol(s,p,b) stoul(s,p,b) stoll(s,p,b) stoull(s,p,b) 返回s的起始子串(表示整数内容)的数值,返回值类型分别是int、long、unsigned long、long long、unsigned long long。b表示转换所用的基数,默认值为10。p是size_t指针,用来保存s中第一个非数值字符的下标,p默认值为0,即函数不保存下标。

stof(s,p) stod(s,p) stold(s,p)  返回s的起始子串(表示浮点数内容)的数值,返回值类型分别是float、double或long double。参数p的作用与整数转换函数中一样。

 

 

9.6 容器适配器

除了顺序容器外,标准库还定义了三个顺序容器适配器:stack、queue和priority_queue。适配器是标准库中的一个通用概念。容器、迭代器和函数都有适配器。本质上,一个适配器是一种机制,能使某种事物的行为看起来像另一种事物一样。

容器适配器底层是顺序容器实现的,如stack可以是deque或vector封住了其他功能,留下部分(从尾部插入、尾部删除、empty、size等)

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

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.swap(b)  交换a和b的内容,a和b必须有相同类型,包括底层容器类型也必须相同。

 

所有适配器都有添加和删除元素的功能:

栈stack的元素操作:

栈默认基于deque实现,也可以在list或vector之上实现。

s.pop()  删除栈顶元素

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

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

 

队列适配器:queue和priority_queue适配器定义在queue文件中。

queue默认基于deque实现,priority_queue默认基于vector实现。

queue也可用list或vector实现,priority_queue也可用deque实现。

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

q.front()  返回首元素,只适用于queue

q.back()  返回尾元素,只适用于queue

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

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

标准库queue使用先进先出FIFO的存储和访问策略。

priority_queue允许我们为队列中的元素建立优先级。新加入的元素会排在所有优先级比它低的已有元素之前。默认情况下,标准库在元素类型上使用<运算符来确定相对优先级。

 

标签:容器,args,string,迭代,STL,适配器,元素,array
来源: https://www.cnblogs.com/cjj-ggboy/p/12402748.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有