c++中容器之总结篇

C++中的容器大致可以分为两个大类:顺序容器和关联容器。顺序容器中有包含有顺序容器适配器。
顺序容器:将单一类型元素聚集起来成为容器,然后根据位置来存储和访问这些元素。主要有vector、list、deque(双端队列)。顺序容器适配器:stack、queue和priority_queue。
关联容器:支持通过键来高效地查找和读取元素。主要有:pair、set、map、multiset和multimap。
接下来依次对于各种容器做详细的介绍。

一、顺序容器

1、顺序容器定义
为了定义一个容器类型的对象,必须先包含相关的头文件:
      定义vector:#include <vector>
      定义list:#include <list>
      定义deque:#include <deque>

定义示例

vector<int> vi;
list<int> li;
deque<int> di;

2、顺序容器初始化

初始化示例:

//初始化为一个容器的副本
vector<int> vi;
vector<int> vi2(vi); //利用vi来初始化vi2
//初始化为一段元素的副本
char*words[] = {"stately", "plump", "buck", "mulligan"};
size_twords_size = sizeof(words)/sizeof(char*);
list<string> words2(words, words + words_size);
//分配和初始化指定数目的元素
constlist<int>::size_type list_size = 64;
list<string> slist(list_size, "a"); // 64 strings, each is a

3、顺序容器支持的指针运算

①所有顺序都支持的指针运行

②vector 和 deque 容器的迭代器提供额外的运算

③迭代器失效:一些容器操作会修改容器的内在状态或移动容器内的元素。这样的操作使所有指向被移动的元素的迭代器失效,也可能同时使其他迭代器失效。使用无效迭代器是没有定义的,可能会导致与悬垂指针相同的问题。

④begin和end成员:begin和end操作产生指向容器内第一个元素和最后一个元素的下一位置的迭代器。

3、顺序容器操作

①添加元素

添加元素示例:

//在容器首部或者尾部添加数据
list<int> ilist;
ilist.push_back(ix);//尾部添加
ilist.push_front(ix);//首部添加
//在容器中指定位置添加元素
list<string> lst;
list<string>::iterator iter = lst.begin();
while (cin >> word)
iter = lst.insert(iter, word); // 和push_front意义一样
//插入一段元素
list<string> slist;
string sarray[4] = {"quasi", "simba", "frollo", "scar"};
slist.insert(slist.end(), 10, "A");//尾部前添加十个元素都是A
list<string>::iterator slist_iter = slist.begin();
slist.insert(slist_iter, sarray+2, sarray+4);//指针范围添加

②容器大小的操作

示例:

list<int> ilist(10, 1);
ilist.resize(15); // 尾部添加五个元素,值都为0
ilist.resize(25, -1); // 再在尾部添加十个元素,元素为-1
ilist.resize(5); // 从尾部删除20个元素

③访问元素

int vector<int> vi;
for(int i=0;i<10;i++)vi.push_back(i);
cout<<vi[0]<<endl;
cout<<vi.at(0)<<endl;
cout<<vi[10]<<endl; //越界错误
cout<<vi.at(10)<<endl;//越界错误

④删除元素

示例:

//删除第一个或最后一个元素
list<int> li;
for(int i=0;i<10;i++)list.push_back(i);
li.pop_front();//删除第一个元素
li.pop_back(); //删除最后一个元素
//删除容器内的一个元素
list<int>::iterator iter =li.begin();
if(iter!= li.end())li.erase(iter);
//删除容器内所有元素
li.clear();

⑤赋值与swap

list<string> sl1,sl2;
for(int i=0;i<10;i++)sl2.push_back("a");
sl1.assign(sl2.begin(),sl2.end());//用sl2的指针范围赋值,sl1中十个元素都为a
sl1.assign(10, "A"); //s1被重新赋值,拥有十个元素,都为A

swap示例:

vector<string> vs1(3); // vs1有3个元素
vector<string> vs(5); // vs2有5个元素
vs1.swap(vs2);//执行后,vs1中5个元素,而vs2则存3个元素。

⑥vector的自增长:capacity 和 reserve 成员

为了提高vector的效率,不用每次添加元素都重新分配空间。vector会在分配空间时候预分配大于需要的空间。vector 类提供了两个成员函数:capacity 和reserve 使程序员可与 vector 容器内存分配的实现部分交互工作。
capacity操作:获取在容器需要分配更多的存储空间之前能够存储的元素总数
reserve操作:告诉vector容器应该预留多少个元素的存储空间

capacity(容量)与size(长度)的区别:size指容器当前拥有的元素个数,而capacity则指容器在必须分配新存储空间之前可以存储的元素总数。capacity是比size大的一般情况下。
示例:

vector<int> ivec;
cout << "ivec: size: " << ivec.size()
<< " capacity: " << ivec.capacity() << endl;//都为0
for (vector<int>::size_type ix = 0; ix != 24; ++ix)ivec.push_back(ix);
cout << "ivec: size: " << ivec.size()
<< " capacity: " << ivec.capacity() << endl;//capacity大于size

可以通过函数reserve()来操作预留空间

//在之前一段代码的基础上
ivec.reserve(ivec.capacity()+50);//为ivec增加了50的预留空间

另外:如果不手动操作来预留空间,每当 vector 容器不得不分配新的存储空间时,以加倍当前容量的分配策略实现重新分配。

4、容器的选用
选择容器类型的常规法则:
①如果程序要求随机访问元素,则应使用 vector 或 deque 容器。
②如果程序必须在容器的中间位置插入或删除元素,则应采用 list 容器。
③如果程序不是在容器的中间位置,而是在容器首部或尾部插入或删除元素,则应采用 deque 容器。
④如果只需在读取输入时在容器的中间位置插入元素,然后需要随机访问元素,则可考虑在输入时将元素读入到一个 list 容器,接着对此容器重新排序,使其适合顺序访问,然后将排序后的 list 容器复制到一个 vector容器。
如果程序既需要随机访问又必须在容器的中间位置插入或删除元素,选择何种容器取决于下面两种操作付出的相对代价:随机访问 list 容器元素的代价,以及在 vector 或 deque 容器中插入/删除元素时复制元素的代价。通常来说,应用中占优势的操作(程序中更多使用的是访问操作还是插入/删除操作)将决定应该什么类型的容器。

5、容器适配器

①适配器通用的操作和类型

②适配器的初始化
所有适配器都定义了两个构造函数:默认构造函数用于创建空对象,而带一个容器参数的构造函数将参数容器的副本作为其基础值。

默认的stack和queue都基于deque容器实现,而priority_queue则在vector容器上实现。
示例:

vector<int> vi;
deque<int> deq;
stack<int> stk(deq); //用deq初始化stk
stack<int> stk1(vi); //报错

③适配器的操作
栈适配器:

队列和优先级队列:

二、关联容器

1、pair

①pairs类型提供的操作

②pairs类型定义和初始化

pair<string, string> test("A", "B");

③pairs其他操作

//pairs对象的操作
string firstBook;
if (author.first == "James" && author.second == "Joyce")firstBook = "Stephen Hero";
//生成新的pair对象
pair<string, string> next_auth;
next_auth = make_pair("A","B");//第一种方法
next_auth = pair<string, string>("A","B"); //第二种方法
cin>>next_auth.first>>next_auth.second;//第三种方法

2、map

map 是键-值对的集合。map 类型通常可理解为关联数组:可使用键作为下标来获取一个值,正如内置数组类型一样。而关联的本质在于元素的值与某个特定的键相关联,而并非通过元素在数组中的位置来获取。

①map对象的定义

map<string, int> word_count;

这个语句定义了一个名为 word_count 的 map 对象,由 string 类型的键索引,关联的值则int型。
map访问:对迭代器进行解引用时,将获得一个引用,指向容器中一个pair<const string,int>类
型的值。通过pair类型的访问方式进行访问。

②map的构造函数

③键类型的约束
在使用关联容器时,它的键不但有一个类型,而且还有一个相关的比较函数。所用的比较函数必须在键类型上定义严格弱排序(strict weak ordering):可理解为键类型数据上的“小于”关系,虽然实际上可以选择将比较函数设计得更复杂。
对于键类型,唯一的约束就是必须支持 < 操作符,至于是否支持其他的关系或相等运算,则不作要求。

④map类定义的类型

value_type是存储元素的键以及值的pair类型,而且键为const。例如,word_count 数组的value_type 为pair<const string,int> 类型。value_type 是 pair 类型,它的值成员可以修改,但键成员不能修改。

⑤map添加元素
添加元素有两种方法:1、先用下标操作符获取元素,然后给获取的元素赋值 2、使用insert成员函数实现
下标操作添加元素:如果该键已在容器中,则 map 的下标运算与 vector 的下标运算行为相同:返回该键所关联的值。只有在所查找的键不存在时,map 容器才为该键创建一个新的元素,并将它插入到此 map 对象中。此时,所关联的值采用值初始化:类类型的元素用默认构造函数初始化,而内置类型的元素初始化为 0。
insert 操作:

示例:

word_count.insert(map<string, int>::value_type("Anna", 1));
word_count.insert(make_pair("Anna", 1));

insert的返回值:包含一个迭代器和一个bool值的pair对象,其中迭代器指向map中具有相应键的元素,而bool值则表示是否插入了该元素。如果该键已在容器中,则其关联的值保持不变,返回的bool值为true。在这两种情况下,迭代器都将指向具有给定键的元素。

pair<map<string, int>::iterator, bool> ret =
word_count.insert(make_pair(word, 1));

ret存储insert函数返回的pair对象。该pair的first成员是一个map迭代器,指向插入的键。ret.first从insert返回的pair对象中获取 map 迭代器;ret.second从insert返回是否插入了该元素。

⑥查找并读取map中的元素
map中使用下标存在一个很危险的副作用:如果该键不在 map 容器中,那么下标操作会插入一个具有该键的新元素。所以map 容器提供了两个操作:count 和 find,用于检查某个键是否存在而不会插入该键。

int occurs = 0;
if (word_count.count("foobar"))occurs = word_count["foobar"];
map<string,int>::iterator it = word_count.find("foobar");
if (it != word_count.end())occurs = it->second;

⑦从map对象中删除元素

string removal_word = "a";
if (word_count.erase(removal_word))
cout << "ok: " << removal_word << " removed\n";
else cout << "oops: " << removal_word << " not found!\n";

3、set

①set容器的定义和使用
set 容器的每个键都只能对应一个元素。以一段范围的元素初始化set对象,或在set对象中插入一组元素时,对于每个键,事实上都只添加了一个元素。

vector<int> ivec;
for (vector<int>::size_type i = 0; i != 10; ++i) {
ivec.push_back(i);
ivec.push_back(i);
}
set<int> iset(ivec.begin(), ivec.end());
cout << ivec.size() << endl; //20个
cout << iset.size() << endl; // 10个

②在set中添加元素

set<string> set1;
set1.insert("the"); //第一种方法:直接添加
set<int> iset2;
iset2.insert(ivec.begin(), ivec.end());//第二中方法:通过指针迭代器

③从set中获取元素
set 容器不提供下标操作符。为了通过键从 set 中获取元素,可使用 find运算。如果只需简单地判断某个元素是否存在,同样可以使用 count 运算,返回 set 中该键对应的元素个数。当然,对于 set 容器,count 的返回值只能是1(该元素存在)或 0(该元素不存在)

set<int> iset;
for(int i = 0; i<10; i++)iset.insert(i);
iset.find(1) // 返回指向元素内容为1的指针
iset.find(11) // 返回指针iset.end()
iset.count(1) // 存在,返回1
iset.count(11) // 不存在,返回0

3、multimap 和 multiset
关联容器 map 和 set 的元素是按顺序存储的。而 multimap 和multset 也一样。因此,在multimap和multiset容器中,如果某个键对应多个实例,则这些实例在容器中将相邻存放。迭代遍历multimap或multiset容器时,可保证依次返回特定键所关联的所有元素。

①迭代器的关联容器操作

以上就是本文的全部内容,希望对大家的学习有所帮助。

(0)

相关推荐

  • C++中用两个标准容器stack,实现一个队列的方法详解

    代码如下所示: 复制代码 代码如下: // StackToQueue.cpp : 定义控制台应用程序的入口点.//用两个标准容器stack,实现一个队列#include "stdafx.h"#include <iostream>#include <stack>using namespace std;template <class T>class StackToQueue{public: StackToQueue() {  stack1;  stack

  • c++ STL容器总结之:vertor与list的应用

    STL提供六大组件,彼此可以组合套用 1.容器(containers):各种数据结构,如vertor,list,deque,set,map.从实现的角度来看,STL容器是一种class template 2.算法(algorithms):各种算法如sort,search,copy,earse.STL算法是一种 function template. 3.迭代器(iterators):扮演容器与算法之间的胶合剂,是所谓的"泛型指针".所有STL容器都有自己的专属的迭代器. 4.仿函数(fu

  • C++利用容器查找重复列功能实现

    复制代码 代码如下: # include <vector> # include <iostream> # include <set> using namespace std; int main(int argc, char * argv[]) { vector<int> v; //找一些数据来测试 for (int i = 0; i < 50; i++) v.push_back(rand() % 25); for (int i = 0; i <

  • C++中的哈希容器unordered_map使用示例

    随着C++0x标准的确立,C++的标准库中也终于有了hash table这个东西. 很久以来,STL中都只提供<map>作为存放对应关系的容器,内部通常用红黑树实现,据说原因是二叉平衡树(如红黑树)的各种操作,插入.删除.查找等,都是稳定的时间复杂度,即O(log n):但是对于hash表来说,由于无法避免re-hash所带来的性能问题,即使大多数情况下hash表的性能非常好,但是re-hash所带来的不稳定性在当时是不能容忍的. 不过由于hash表的性能优势,它的使用面还是很广的,于是第三方

  • c++中容器之总结篇

    C++中的容器大致可以分为两个大类:顺序容器和关联容器.顺序容器中有包含有顺序容器适配器. 顺序容器:将单一类型元素聚集起来成为容器,然后根据位置来存储和访问这些元素.主要有vector.list.deque(双端队列).顺序容器适配器:stack.queue和priority_queue. 关联容器:支持通过键来高效地查找和读取元素.主要有:pair.set.map.multiset和multimap. 接下来依次对于各种容器做详细的介绍. 一.顺序容器 1.顺序容器定义 为了定义一个容器类型

  • docker中容器数据卷volume介绍

    目录 docker 容器数据卷volume 使用数据卷 方式一:直接使用命令挂载 -v 方式二:Dockerfile 数据卷容器 总结 docker 容器数据卷volume 如果数据都在容器中,那么我们删除容器的时候数据就会丢失,所以我们希望数据可以持久化. 例如MySQL容器,我们希望数据可以存储在本地,当MySQL容器删除的时候,数据不会丢失. 容器之间可以有一个数据共享的技术,Docker容器中产生的数据,同步到本地,这就是卷技术.也就是数据挂载技术,将我们容器内的目录,挂载到Linux上

  • 聊聊Docker中容器的创建与启停问题

    目录 1. 镜像和容器 2. 新建并启动容器 3. 使用第一个容器 4. 容器命名 5.重启容器 6. 附着到容器上 1. 镜像和容器 看待镜像和容器的一种方式是将它们类比成程序与进程.一个进程可以视为一个被执行的应用程序,同样,一个Docker容器可以视为一个运行中的Docker镜像. 标题Docker镜像与容器 如果大家熟悉面向对象原理,看待镜像和容器的另一种方法是将镜像看作类而将容器看作对象.对象是类的具体实例,同样,容器是镜像的实例.用户可以从单个镜像创建多个容器,就像对象一样,它们之间

  • Docker中容器数据卷详解

    目录 什么是容器数据卷 数据的覆盖问题 使用数据卷 方式一:直接使用命令挂载 -v 测试挂载卷 方式二:Dockerfile文件 数据卷命令 查看数据卷 方式一:docker inspect 容器ID 方式二:docker volume inspect juming-nginx 挂载三种方式 扩展 什么是容器数据卷 从docker的理念说起,docker将应用和环境打包成一个镜像,运行镜像(生成容器)就可以访问服务了. 如果数据都存在容器中,那么删除容器,数据就会丢失!需求:数据可以持久化 My

  • 全面解析JavaScript中“&&”和“||”操作符(总结篇)

    1.||(逻辑或), 从字面上来说,只有前后都是false的时候才返回false,否则返回true. alert(true||false); // true alert(false||true); // true alert(true||true); // true alert(false||false); // false 这个傻子都知道~~ 但是,从深层意义上来说的话,却有另一番天地,试下面代码 alert(0||1);//1 显然,我们知道,前面0意味着false,而后面1意味着true,

  • thinkphp5.1框架中容器(Container)和门面(Facade)的实现方法分析

    本文实例讲述了thinkphp5.1框架中容器(Container)和门面(Facade)的实现方法.分享给大家供大家参考,具体如下: tp5.1中引入了容器(Container)和门面(Facade)这两个新的类 官方文档已经给出了定义: 容器(Container)实现类的统一管理,确保对象实例的唯一性. 门面(Facade)为容器(Container)中的类提供了一个静态调用接口,相比于传统的静态方法调用, 带来了更好的可测试性和扩展性,你可以为任何的非静态类库定义一个facade类. 深入

  • 聊聊docker中容器与镜像的区别

    什么是镜像? 镜像可以看成是由多个镜像层叠加起来的一个文件系统(通过UnionFS与AUFS文件联合系统实现),镜像层也可以简单理解为一个基本的镜像,而每个镜像层之间通过指针的形式进行叠加. 什么是容器? 容器(container)的定义和镜像(image)几乎一模一样,也是一堆层的统一视角,唯一区别在于容器的最上面那一层是可读可写的.要点:容器 = 镜像 + 读写层,并且容器的定义并没有提及是否要运行容器. 今天抛开原理,抛开底层.通俗的讲解docker中容器与镜像的区别. 对于初学者来说,刚

  • java中容器(顶层容器和中间容器)的布局管理器详解

    目录 java容器(顶层容器和中间容器)的布局管理器 一.布局管理器所属类包 二.容器的默认布局管理器 java常用的四大容器总结 一.为什么要使用容器(集合类)? 二.Java中四大容器的简介与区别 三.Java的容器体系 java容器(顶层容器和中间容器)的布局管理器 Java能够以像素为单位对组件进行精确的定位,但是其在不同的系统中将会有一定的显示差异,使得显示效果不同,为此java提供了布局管理器,使编写的图形界面具有良好的平台无关性. 注意:所有的布局管理器均是针对容器来使用的,包括顶

  • Docker中容器数据卷(Data Volume)和数据管理详解

    卷(Volume) 众所周知卷(Volume)是容器中的一个数据挂载点,卷可以绕过联合文件系统,从而为Docker 提供持久数据,所提供的数据还可以在宿主机-容器或多个容器之间共享.通过卷,我们可以可以使修改数据直接生效,而不必重新构建镜像. 一.数据卷 数据卷是一个可以绕过联合文件系统的,专门指定的可在一或多个容器间共享目录.卷为提供为持久化或共享数据提供了一些有用的特性. 数据卷设计的初哀是提供持久化数据,而与容器的生命周期无关.因此,在删除容器时,Docker不会自动删除卷,直到没有容器再

  • 详解Docker中容器的备份、恢复和迁移

    今天,我们将学习如何快速地对docker容器进行快捷备份.恢复和迁移.Docker是一个开源平台,用于自动化部署应用,以通过快捷的途径在称之为容器的轻量级软件层下打包.发布和运行这些应用.它使得应用平台独立,因为它扮演了 Linux上一个额外的操作系统级虚拟化的自动化抽象层.它通过其组件cgroups和命名空间利用Linux内核的资源分离特性,达到避免虚拟机开销的目的.它使得用于部署和扩展web应用.数据库和后端服务的大规模构建组件无需依赖于特定的堆栈或供应者. 所谓的容器,就是那些创建自Do

随机推荐