您的位置:首页 > 编程语言 > C语言/C++

C++语法——容器类(list, vector, map, set)

2016-07-19 17:21 351 查看

0 所有容器类

0.1 顺序容器

vector(可变大小数组)

将元素保存在连续的内存空间中,可以用下标(即[]运算符)来读取元素。

因此随机访问速度很快。

尾部插入/删除元素很快,但在头部或中间位置插入/删除元素很慢。

保留备份空间,在超过容量额度时重新全部分配内存,导致迭代器失效。所以当添加一个元素时可能需要重新分配内存并移动每个元素的位置,开销较大。

list(双向链表)

元素存放在链表中,不能用下标([])读取元素。

任何位置的元素添加和删除都很快。

不支持随机访问,想要访问一个元素需要遍历整个容器。

没有备份空间的概念,出入一个元素就申请一个元素的空间,所以它的迭代器不会失效。

forwar_list(单向链表)

与list类似,不同的是list可以双向顺序访问,而forward_list只能单向顺序访问。

另外,forward_list不支持size操作(.size() —— 读取元素的个数),因为保存或计算其大小会比手写链表多出额外的开销。

array(固定大小数组)

与内置数组类似,但更安全、更易使用。

大小是固定的。

deque(双端队列)

支持快速随机访问。

在头、尾位置插入/删除元素很快,但在中间位置插入/删除元素很慢。

string(可变大小的字符数组)

与vector类似,不过专门用于保存字符。

0.2 关联容器

1 vector

向量 相当于一个数组

在内存中分配一块连续的内存空间进行存储。支持不指定vector大小的存储。STL内部实现时,首先分配一个非常大的内存空间预备进行存储,即capacituy()函数返回的大小,当超过此分配的空间时再整体重新放分配一块内存存储,这给人以vector可以不指定vector即一个连续内存的大小的感觉。通常此默认的内存分配能完成大部分情况下的存储。

优点:

(1) 不指定一块内存大小的数组的连续存储,即可以像数组一样操作,但可以对此数组

进行动态操作。通常体现在push_back() pop_back()

(2) 随机访问方便,即支持[ ]操作符和vector.at()

(3) 节省空间。

缺点:

(1) 在内部进行插入删除操作效率低。

(2) 只能在vector的最后进行push和pop,不能在vector的头进行push和pop。

(3) 当动态添加的数据超过vector默认分配的大小时要进行整体的重新分配、拷贝与释



2 list

双向链表

每一个结点都包括一个信息快Info、一个前驱指针Pre、一个后驱指针Post。可以不分配必须的内存大小方便的进行添加和删除操作。使用的是非连续的内存空间进行存储。

优点:

(1) 不使用连续内存完成动态操作。

(2) 在内部方便的进行插入和删除操作

(3) 可在两端进行push、pop

缺点:(1) 不能进行内部的随机访问,即不支持[ ]操作符和vector.at()

(2) 相对于verctor占用内存多

3 deque

双端队列 double-end queue

deque是在功能上合并了vector和list。

优点:

(1) 随机访问方便,即支持[ ]操作符和vector.at()

(2) 在内部方便的进行插入和删除操作

(3) 可在两端进行push、pop

缺点:(1) 占用内存多

使用区别:

1 如果你需要高效的随即存取,而不在乎插入和删除的效率,使用vector

2 如果你需要大量的插入和删除,而不关心随即存取,则应使用list

3 如果你需要随即存取,而且关心两端数据的插入和删除,则应使用deque

1 双向循环链表list

添加头文件< list>。使用时,std::list

Lists将元素按顺序储存在链表中,与向量(vector)相比,它允许快速的插入和删除,但是随机访问却比较慢。不支持[]运算符。和vector另一点不同的是,list的迭代器不会存在失效的情况,他不像vector会保留备份空间,在超过容量额度时重新全部分配内存,导致迭代器失效;list没有备份空间的概念,出入一个元素就申请一个元素的空间,所以它的迭代器不会失效。

list是双向循环链表,每一个元素都知道前面一个元素和后面一个元素。在STL中,list和vector一样,是两个常被使用的容器。list中提供的成员函数与vector类似,不过list提供对表首元素的操作push_front、pop_front,这是vector不具备的。

int data[6]={3,5,7,9,2,4};
list<int> lidata(data, data+6);
lidata.push_back(6);
...


list初始化时,申请的空间大小为6,存放下了data中的6个元素,当向lidata插入第7个元素“6”时,list申请新的节点单元,插入到list链表中,数据存放结构如图:



list每次增加一个元素,不存在重新申请内存的情况,它的成本是恒定的。而vector每当增加关键元素的时候,都需要重新申请新的更大的内存空间,会调用元素的自身的复制构造函数,存在构造成本。在销毁旧内存的时候,会调用析构函数,存在析构成本。所以在存储复杂类型和大量元素的情况下,list比vector更有优势!

List是一个双向链表,双链表既可以向前又向后链接他的元素。

List将元素按顺序储存在链表中. 与 向量(vector)相比, 它允许快速的插入和删除,但是随机访问却比较慢。

assign() 给list赋值

back() 返回最后一个元素

begin() 返回指向第一个元素的迭代器

clear() 删除所有元素

empty() 如果list是空的则返回true

end() 返回末尾的迭代器

erase() 删除一个元素

front() 返回第一个元素

get_allocator() 返回list的配置器

insert() 插入一个元素到list中

max_size() 返回list能容纳的最大元素数量

merge() 合并两个list

pop_back() 删除最后一个元素

pop_front() 删除第一个元素

push_back() 在list的末尾添加一个元素

push_front() 在list的头部添加一个元素

rbegin() 返回指向第一个元素的逆向迭代器

remove() 从list删除元素

remove_if() 按指定条件删除元素

rend() 指向list末尾的逆向迭代器

resize() 改变list的大小

reverse() 把list的元素倒转

size() 返回list中的元素个数

sort() 给list排序

splice() 合并两个list

swap() 交换两个list

unique() 删除list中重复的元素

2 vector

(一) 头文件

include < vector>

using std::vector;

(二)实例化:

vector< int> ivec; // ivec保存int类型的对象

vector< Sales_item> SalesVec; // 保存Sales_item类型的对象

vector< vector> file; // 保存vector对象((创建二维向量))

老式的编译器中需要添加一个空格vector< vector< int> > file;

初始化的形式:

vector< int> a ; //声明一个int型向量a

vector< int> a(10) ; //声明一个初始大小为10的向量

vector< int> a(10, 1) ; //声明一个初始大小为10且初始值都为1的向量

vector< int> b(a) ; //声明并用向量a初始化向量b

vector< int> b = a ; //声明并用向量a初始化向量b

vector< int> b{1,2,3,4}; //列表初始化:声明一个初始大小为4的向量,其元素为1,2,3,4

vector< int> b={1,2,3,4}; //同上

vector< int> b(a.begin(), a.begin()+3) ; //将a向量中从第0个到第2个(共3个)作为向量b的初始值

最常见的是先定义一个空vector,然后再运行时获取到元素的值之后在逐一添加。

(三)元素的输入和访问:

元素的输入和访问可以像操作普通的数组那样, 用cin>>进行输入, cout<< a
这样进行输出。

(四)操作

需要注意:范围for循环语句体内不应该改变其所遍历序列的大小!!

1>a.size() //返回向量中的元素个数

2>a.empty() //判断向量是否为空,空则返回真

3>a.clear() //清空向量中的元素

4>复制

a = b ; //将b向量复制到a向量中

5>比较

保持 ==、!=、>、>=、<、<= 的惯有含义 ;

如: a == b ; //a向量与b向量比较, 相等则返回1

6>插入 - insert

①、 a.insert(a.begin(), 1000); //将1000插入到向量a的起始位置前

②、 a.insert(a.begin(), 3, 1000) ; //将1000分别插入到向量元素位置的0-2处(共3个元素)

③、 vector a(5, 1) ;

vector b(10) ;

b.insert(b.begin(), a.begin(), a.end()) ; //将a.begin(), a.end()之间的全部元素插入到b.begin()前

7>删除 - erase

①、 b.erase(b.begin()) ; //将起始位置的元素删除

②、 b.erase(b.begin(), b.begin()+3) ; //将(b.begin(), b.begin()+3)之间的元素删除

8>交换 - swap

b.swap(a) ; //a向量与b向量进行交换

9>a
返回a中第n个位置上元素的引用

(五)vector的元素不仅仅可以使int,double,string,还可以是结构体,但是要注意:结构体要定义为全局的,否则会出错。

(六)算法

1) 使用reverse将元素翻转:需要头文件#include

reverse(vec.begin(),vec.end());将元素翻转在vector中,如果一个函数中需要两个迭代器,一般后一个都不包含

(2)使用sort排序:需要头文件#include

sort(vec.begin(),vec.end());(默认是按升序排列,即从小到大).

可以通过重写排序比较函数按照降序比较,如下:

定义排序比较函数:

bool Comp(const int &a,const int &b)

{

return a>b;

}

调用时:sort(vec.begin(),vec.end(),Comp),这样就降序排序。

3 map

3.1 map简介

map是一类关联式容器。它的特点是增加和删除节点对迭代器的影响很小,除了那个操作节点,对其他的节点都没有什么影响。对于迭代器来说,可以修改实值,而不能修改key。

3.2 map的功能

自动建立Key - value的对应。key 和 value可以是任意你需要的类型。

根据key值快速查找记录,查找的复杂度基本是Log(N),如果有1000个记录,最多查找10次,1,000,000个记录,最多查找20次。

快速插入Key - Value 记录。

快速删除记录

根据Key 修改value记录。

遍历所有记录。

3.3 使用map

使用map得包含map类所在的头文件

#include <map> //注意,STL头文件没有扩展名.h


map对象是模板类,需要关键字和存储对象两个模板参数:

std:map<int, string> personnel;


这样就定义了一个用int作为索引,并拥有相关联的指向string的指针。

为了使用方便,可以对模板类进行一下类型定义:

typedef map<int, CString> UDT_MAP_INT_CSTRING;
UDT_MAP_INT_CSTRING enumMap;


3.4 在map中插入元素

改变map中的条目非常简单,因为map类已经对[]操作符进行了重载

enumMap[1] = “One”;

enumMap[2] = “Two”;

…..

这样非常直观,但存在一个性能的问题。插入2时,先在enumMap中查找主键为2的项,没发现,然后将一个新的对象插入enumMap,键是2,值是一个空字符串,插入完成后,将字符串赋为”Two”; 该方法会将每个值都赋为缺省值,然后再赋为显示的值,如果元素是类对象,则开销比较大。我们可以用以下方法来避免开销

enumMap.insert(map<int, CString>::value_type(2, "Two"))


3.5 查找并获取map中的元素

下标操作符给出了获得一个值的最简单方法:

CString tmp = enumMap[2];

但是,只有当map中有这个键的实例时才对,否则会自动插入一个实例,值为初始化值

我们可以使用Find()和Count()方法来发现一个键是否存在。

查找map中是否包含某个关键字条目用find()方法,传入的参数是要查找的key,在这里需要提到的是begin()和end()两个成员,分别代表map对象中第一个条目和最后一个条目,这两个数据的类型是iterator.

int nFindKey = 2; //要查找的Key
UDT_MAP_INT_CSTRING::iterator it= enumMap.find(nFindKey);//定义一个条目变量(实际是指针)
if(it == enumMap.end())
{
//没找到
}
else
{
//找到
}


通过map对象的方法获取的iterator数据类型是一个std::pair对象,包括两个数据 iterator->first 和 iterator->second 分别代表关键字和存储的数据

3.6 从map中删除元素

移除某个map中某个条目用erase()

该成员方法的定义如下

iterator erase(iterator it); //通过一个条目对象删除

iterator erase(iterator first, iterator last); //删除一个范围

size_type erase(const Key& key); //通过关键字删除

clear()就相当于 enumMap.erase(enumMap.begin(), enumMap.end());

3.7 map的基本操作函数

C++ Maps是一种关联式容器,包含“关键字/值”对
begin()          返回指向map头部的迭代器
clear()         删除所有元素
count()          返回指定元素出现的次数
empty()          如果map为空则返回true
end()            返回指向map末尾的迭代器
equal_range()    返回特殊条目的迭代器对
erase()          删除一个元素
find()           查找一个元素
get_allocator()  返回map的配置器
insert()         插入元素
key_comp()       返回比较元素key的函数
lower_bound()    返回键值>=给定元素的第一个位置
max_size()       返回可以容纳的最大元素个数
rbegin()         返回一个指向map尾部的逆向迭代器
rend()           返回一个指向map头部的逆向迭代器
size()           返回map中元素的个数
swap()            交换两个map
upper_bound()     返回键值>给定元素的第一个位置
value_comp()      返回比较元素value的函数


例子:

//遍历:

map<string,CAgent>::iterator iter;
for(iter = m_AgentClients.begin(); iter != m_AgentClients.end(); ++iter)
{
if(iter->first=="8001"  {
  this->SendMsg(iter->second.pSocket,strMsg);//iter->first
}
}

//查找:

map<string,CAgent>::iterator iter=m_AgentClients.find(strAgentName);
if(iter!=m_AgentClients.end())//有重名的  {
}
else //没有{
}

//元素的个数

if (m_AgentClients.size()==0)

//删除

map<string,CAgent>::iterator iter=m_AgentClients.find(pSocket->GetName());
if(iter!=m_AgentClients.end())
{

m_AgentClients.erase(iter);//列表移除
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: