首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >C++(STL):34--- multiset容器详解

C++(STL):34--- multiset容器详解

作者头像
用户3479834
发布于 2021-02-03 06:49:14
发布于 2021-02-03 06:49:14
1.3K00
代码可运行
举报
文章被收录于专栏:游戏开发司机游戏开发司机
运行总次数:0
代码可运行

前面章节中,对 set 容器做了详细的讲解。回忆一下,set 容器具有以下几个特性:

  • 不再以键值对的方式存储数据,因为 set 容器专门用于存储键和值相等的键值对,因此该容器中真正存储的是各个键值对的值(value);
  • set 容器在存储数据时,会根据各元素值的大小对存储的元素进行排序(默认做升序排序);
  • 存储到 set 容器中的元素,虽然其类型没有明确用 const 修饰,但正常情况下它们的值是无法被修改的;
  • set 容器存储的元素必须互不相等。

在此基础上,C++ STL 标准库中还提供有一个和 set 容器相似的关联式容器,即 multiset 容器。所谓“相似”,是指 multiset 容器遵循 set 容器的前 3 个特性,仅在第 4 条特性上有差异。和 set 容器不同的是,multiset 容器可以存储多个值相同的元素。

也就是说,multiset 容器和 set 容器唯一的差别在于,multiset 容器允许存储多个值相同的元素,而 set 容器中只能存储互不相同的元素。

和 set 类模板一样,multiset 类模板也定义在<set>头文件,并位于 std 命名空间中。这意味着,如果想在程序中使用 multiset 容器,该程序代码应包含如下语句:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <set>using namespace std;

注意,第二行代码不是必需的,如果不用,则后续程序中在使用 multiset容器时,需手动注明 std 命名空间(强烈建议初学者使用)。

multiset 容器类模板的定义如下所示:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
template < class T,                        // 存储元素的类型
class Compare = less<T>,        // 指定容器内部的排序规则
class Alloc = allocator<T> >    // 指定分配器对象的类型
> class multiset;

显然,multiset 类模板有 3 个参数,其中后 2 个参数自带有默认值。值得一提的是,在实际使用中,我们最多只需要使用前 2 个参数即可,第 3 个参数不会用到。

创建C++ multiset容器的方法

创建 multiset 容器,无疑需要调用 multiset 类模板中的构造函数。值得一提的是,multiset 类模板提供的构造函数,和 set 类模板中提供创建 set 容器的构造函数,是完全相同的。这意味着,创建 set 容器的方式,也同样适用于创建 multiset 容器。 考虑到一些读者可能并未系统学习 set 容器,因此这里还是对 multiset 容器的创建做一下详细的介绍。 multiset 类模板中提供了 5 种构造函数,也就代表有 5 种创建 multiset 容器的方式,分别如下。 1) 调用默认构造函数,创建空的 multiset 容器。比如:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
std::multiset<std::string> mymultiset;

如果程序中已经默认指定了 std 命令空间,这里可以省略 std::。

由此就创建好了一个 mymultiset 容器,该容器采用默认的std::less<T>规则,会对存储的 string 类型元素做升序排序。

注意,由于 multiset 容器支持随时向内部添加新的元素,因此创建空 multiset 容器的方法比较常用。

2)除此之外,multiset 类模板还支持在创建 multiset 容器的同时,对其进行初始化。例如:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
std::multiset<std::string> mymultiset{ "C++","node.js","php" };

由此即创建好了包含 3 个 string 元素的 mymultiset 容器。由于其采用默认的std::less<T>规则,因此其内部存储 string 元素的顺序如下所示:

3) multiset 类模板中还提供了拷贝(复制)构造函数,可以实现在创建新 multiset 容器的同时,将已有 multiset 容器中存储的所有元素全部复制到新 multiset 容器中。 例如,在第 2 种方式创建的 mymultiset 容器的基础上,执行如下代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
std::multiset<std::string> copymultiset(mymultiset);//等同于//std::multiset<std::string> copymultiset = mymultiset;

该行代码在创建 copymultiset 容器的基础上,还会将 mymultiset 容器中存储的所有元素,全部复制给 copymultiset 容器一份。 另外,C++ 11 标准还为 multiset 类模板新增了移动构造函数,其功能是实现创建新 multiset 容器的同时,利用临时的 multiset 容器为其初始化。比如:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
multiset<string> retMultiset() {
std::multiset<std::string> tempmultiset{ "C++",
"node.js",
"php" };
return tempmultiset;
}
std::multiset<std::string> copymultiset(retMultiset());
//等同于
//std::multiset<std::string> copymultiset = retMultiset();

注意,由于 retMultiset() 函数的返回值是一个临时 multiset 容器,因此在初始化 copymultiset 容器时,其内部调用的是 multiset 类模板中的移动构造函数,而非拷贝构造函数。

显然,无论是调用复制构造函数还是调用拷贝构造函数,都必须保证这 2 个容器的类型完全一致。

4) 在第 3 种方式的基础上,multiset 类模板还支持取已有 multiset 容器中的部分元素,来初始化新 multiset 容器。例如:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制


std::multiset<std::string> mymultiset{ "C++",
"node.js",
"php" };
std::set<std::string> copymultiset(++mymultiset.begin(), mymultiset.end());

以上初始化的 copyset 容器,其内部仅存有如下 2 个 string 字符串:

5) 以上几种方式创建的 multiset 容器,都采用了默认的std::less<T>规则。其实,借助 multiset 类模板定义中的第 2 个参数,我们完全可以手动修改 multiset 容器中的排序规则。 下面样例中,使用了 STL 标准库提供的 std::greater<T> 排序方法,作为 multiset 容器内部的排序规则:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <string>
#include <set>
using namespace std;
int main() {
  std::multiset<std::string, std::greater<string>> mymultiset{ "C++",
"node.js","php"}; 
}

通过选用std::greater<string>降序规则,mymultiset 容器中元素的存储顺序为:

C++ multiset容器提供的成员方法

multiset 容器提供的成员方法,和 set 容器提供的完全一样,如表 1 所示。

成员方法

功能

begin()

返回指向容器中第一个(注意,是已排好序的第一个)元素的双向迭代器。如果 multiset 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。

end()

返回指向容器最后一个元素(注意,是已排好序的最后一个)所在位置后一个位置的双向迭代器,通常和 begin() 结合使用。如果 multiset 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。

rbegin()

返回指向最后一个(注意,是已排好序的最后一个)元素的反向双向迭代器。如果 multiset 容器用 const 限定,则该方法返回的是 const 类型的反向双向迭代器。

rend()

返回指向第一个(注意,是已排好序的第一个)元素所在位置前一个位置的反向双向迭代器。如果 multiset 容器用 const 限定,则该方法返回的是 const 类型的反向双向迭代器。

cbegin()

和 begin() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改容器内存储的元素值。

cend()

和 end() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改容器内存储的元素值。

crbegin()

和 rbegin() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改容器内存储的元素值。

crend()

和 rend() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改容器内存储的元素值。

find(val)

在 multiset 容器中查找值为 val 的元素,如果成功找到,则返回指向该元素的双向迭代器;反之,则返回和 end() 方法一样的迭代器。另外,如果 multiset 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。

lower_bound(val)

返回一个指向当前 multiset 容器中第一个大于或等于 val 的元素的双向迭代器。如果 multiset 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。

upper_bound(val)

返回一个指向当前 multiset 容器中第一个大于 val 的元素的迭代器。如果 multiset 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。

equal_range(val)

该方法返回一个 pair 对象(包含 2 个双向迭代器),其中 pair.first 和 lower_bound() 方法的返回值等价,pair.second 和 upper_bound() 方法的返回值等价。也就是说,该方法将返回一个范围,该范围中包含所有值为 val 的元素。

empty()

若容器为空,则返回 true;否则 false。

size()

返回当前 multiset 容器中存有元素的个数。

max_size()

返回 multiset 容器所能容纳元素的最大个数,不同的操作系统,其返回值亦不相同。

insert()

向 multiset 容器中插入元素。

erase()

删除 multiset 容器中存储的指定元素。

swap()

交换 2 个 multiset 容器中存储的所有元素。这意味着,操作的 2 个 multiset 容器的类型必须相同。

clear()

清空 multiset 容器中所有的元素,即令 multiset 容器的 size() 为 0。

emplace()

在当前 multiset 容器中的指定位置直接构造新元素。其效果和 insert() 一样,但效率更高。

emplace_hint()

本质上和 emplace() 在 multiset 容器中构造新元素的方式是一样的,不同之处在于,使用者必须为该方法提供一个指示新元素生成位置的迭代器,并作为该方法的第一个参数。

count(val)

在当前 multiset 容器中,查找值为 val 的元素的个数,并返回。

注意,虽然 multiset 容器和 set 容器拥有的成员方法完全相同,但由于 multiset 容器允许存储多个值相同的元素,因此诸如 count()、find()、lower_bound()、upper_bound()、equal_range()等方法,更常用于 multiset 容器。

下面程序演示了表 1 中部分成员函数的用法:

代码语言:javascript
代码运行次数:0
运行
复制
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <iostream>
#include <set>
#include <string>
using namespace std;


int main() {
std::multiset<int> mymultiset{1,2,2,2,3,4,5};
cout << "multiset size = " << mymultiset.size() << endl;
cout << "multiset count(2) =" << mymultiset.count(2) << endl;
//向容器中添加元素 8
mymultiset.insert(8);
//删除容器中所有值为 2 的元素
int num = mymultiset.erase(2);
cout << "删除了 " << num << " 个元素 2" << endl;
//输出容器中存储的所有元素
for (auto iter = mymultiset.begin(); iter != mymultiset.end(); ++iter) {
cout << *iter << " ";
}
return 0;
}

程序执行结果为:

multiset size = 7 multiset count(2) =3 删除了 3 个元素 2 1 3 4 5 8

注意,表 1 中大多数成员方法的用法,和 set 容器中相应成员方法的用法是完全一样的,只是调用者不同。因此,如果读者想详细了解表 1 中某个成员方法的用法,可以阅读讲解 set 容器相同成员方法的文章。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-12-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 游戏开发司机 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
Kotlin 语言中的“关键字” Keywords in Kotlin修饰符关键字
但在 kotlin, 有一些关键字在某些情况下可以用作标识符。 在 kotlin 中基本上有四种类型的关键字:
一个会写诗的程序员
2018/12/06
8410
Kotlin 语言中的“关键字”  Keywords in Kotlin修饰符关键字
JAVA基础语法——标识符、修饰符、关键字(个人整理总结)
多个单词组成时第一个单词首字母小写,其他单词首字母大写(例:lastAccessTime、getTime)。
泰斗贤若如
2019/08/07
4.8K0
JAVA基础语法——标识符、修饰符、关键字(个人整理总结)
Kotlin学习之路(2)数据类型
每一种语言都有自己的基本数据类型,Kotlin也有自己的数据类型,类似与Java包括整型 浮点型 布尔类型等。
全栈程序员站长
2021/04/07
8450
【Pyhton学习】常用标识符与关键字
在Python中,标识符是程序员定义的名称,用于标识变量、函数、类等。标识符需遵循以下规则:
鸽芷咕
2025/05/20
1050
【Pyhton学习】常用标识符与关键字
java 标识符,分隔符,关键字[通俗易懂]
Java语言中,对于变量,常量,函数,语句块也有名字,我们统统称之为Java标识符. 标识符是用来给类、对象、方法、变量、接口和自定义数据类型命名的。
全栈程序员站长
2022/09/08
8250
《Java从入门到失业》第三章:基础语法及基本程序结构(3.2-3.5):标识符、关键字、注释、变量及常量
       上面我们知道我们自定义一个类,需要一个类名。在Java中,还有很多需要命名的组成部分,例如方法名,变量名等。标识符的命名需要遵循Java的规范,总结如下:
用户7801119
2020/09/27
4150
2.1 java基础语法之关键字和标识符
大家好,我们在前面的几篇博客中已经给大家介绍了java的环境安装,第一个java程序,和java环境变量的配置,那么我们这一章节主要给大家介绍一个java中的基础语法,也就是java这门语言当中的一些基本用法,java中的基础语法主要包括java中的关键字和标识符,java中的变量和数据类型,java中的运算符,java中的流程控制语句。大概分为这么4大模块,我们会分别介绍,本篇文章我们主要来研究一下java中的关键字和标识符。
一缕82年的清风
2022/01/10
2560
java标识符与关键字_4、Java标识符和关键字
标识符:Java对各种变量,方法和类等要素命名时使用的字符序列称为标识符。(凡是自己可以起名的地方都叫标识符,都遵循标识符的规则)
全栈程序员站长
2022/09/08
3440
Java基础入门篇(二)——Java注释、关键字和标识符
前面几篇文章用Java带大家一起了解了几个游戏小项目,感兴趣的小伙伴可以点击文章观摩下,手把手教你用Java打造一款简单故事书(上篇)、手把手教你用Java打造一款简单故事书(下篇)、手把手教你用Java打造一款简单考试系统(上篇)、手把手教你用Java打造一款简单考试系统(下篇)接下来的几篇文章是关于Java基础的,希望对大家的学习有帮助,欢迎大家在讨论区留言。
Java进阶者
2021/01/22
5710
【python基础教程】关键字与标识符
关键字是python语言中一些已经被赋予特定意义的单词。开发程序是,不可以把这些关键字作文变量、函数、类、模块和其他对象的名称来使用。python语言中的关键字如下表所示
hacker707
2022/11/27
3980
【python基础教程】关键字与标识符
Java标识符与关键字
1.Java注释 ☞编码加上注释!!!!  ● 单行注释:// 注释内容 (最常用)  ● 多行注释:/* 注释内容 / (不推荐)  ● 文档注释:/* 文档注释 */ (常见于方法和类之上描述方法和类的作用),可自动生成文档 2.Java标识符 Java中,对于变量、常量、函数、语句块都有名字,统称为Java标识符。 ◆对标识符的三点要求:  ●标识符由字母、数字、_(下划线)、$组成,不能以数字开头,不能用Java中的关键字  ●标识符采用有意义的简单命名  ●“$”不要在代码中出现 ◆驼峰命名法: 大驼峰:定义类、接口时使用   单词以大写字母开头,若有多个单词,每个单词首字母大写      public class FirstClass 小驼峰:定义变量、函数时使用   若标识符只有一个单词,全小写;若标识符由多个单词组成,从第二个单词开始首字母大写     int mathScore = 10 常量:所有单词全部大写,多个单词间以 - 分隔 3.关键字 注意:  ●Java中有两个未使用的保留字:goto、const  ●Java中有三个特殊含义的单词:null、ture、false  ●JDK1.4后追加了 assert关键字;JDK1.5以后追加了enum关键字  4.数据类型划分 注意:对数据类型的选择  ●在程序开发之中,整数就用int,描述小数用double。  ●long一般用于描述日期、时间、内存或文件大小(字节)  ●如果要进行编码转换或者进行二进制流的操作,使用byte(-127~128)  ●char一般在描述中文中会用到(基本忽略)   4.1 基本数据类型(八大基本类型) 4.1.1 数值型 整型:默认值0 byte(-128~127)<short < int(-231~231) < long 在Java中,任何一个整型常量都是int类型 当数据类型达到最大值时,换一个保存范围更大的类型来解决数据溢出问题 Java中声明long常量,需要在数字后加 l 或 L 在进行数学计算时,小的数据类型自动转为大的数据类型,大的数据类型变为小的数据类型必须强制类型转换,可能会溢出。(最高位取反) byte(-128~127)与int类型: 当整型常量在byte保存范围中,可以直接赋值给byte变量;常量大小超出byte范围,int变量赋值给byte变量,所有赋值必须强转。
用户7886150
2020/12/02
3630
Java基础之关键字,标识符,注释,数据类型
Java的关键字对java的编译器有特殊的意义,他们用来表示一种数据类型,或者表示程序的结构等。
南风
2019/04/22
4340
【面试题精讲】标识符和关键字的区别是什么
在上面的示例中,myVariable是一个标识符,用来表示一个整数类型的变量。if是一个关键字,用于控制程序的流程。
程序员朱永胜
2023/09/28
1.3K0
不可不看的Java基础知识整理,注释、关键字、运算符
万丈高楼平地起,要想学好汉语首先学拼音,想学好英语首先学26个字母,对于编程语言来说,一样的道理,要想学好必须先掌握其基础语法和知识,今天我们就来唠一唠Java语言中那些出现频率极高,又很基础的知识点吧!
JavaBuild
2024/05/27
970
不可不看的Java基础知识整理,注释、关键字、运算符
【初识Go】| Day2 数据类型、关键字、标识符
数据类型的出现是为了把数据分成所需内存大小不同的数据,编程的时候需要用大数据的时候才需要申请大内存,就可以充分利用内存。
yussuy
2020/12/16
5980
【初识Go】| Day2 数据类型、关键字、标识符
Java 基础标识符
标识符: 程序员为自己定义的类,方法或者变量等起的名称。     标识符由大写字母,数字,下划线(_)和美元符号组成,但不能以数字开头。 Java 语言中严格区分大小写。     包名: 使用小写字母。         类名和接口名: 通常定义为由具有含义的单词组成,所有单词的首字母大写。   方法名: 通常也是由具有含义的单词组成,第一个单词首字母小写,其他单词的首字母都大写。   变量名: 成员变量和方法相同,局部变量全部使用小写。   常量名: 全部使用大写, 最后已用下划线分割单词     关键字:
用户1197315
2018/01/19
8550
Java 基础语法(1)- 注释、标识符、关键字
背景 要开始磕 Java 了,虽然以前学过用过,但是差不多忘光光了... 现在直接搬狂神的视频素材,不再自己总结,要学的东西太多了... 注释 单行注释 // 多行注释 /* */ 文档
小菠萝测试笔记
2021/07/08
4510
Java 基础教学:基础语法 - 注释、标识符与关键字
Java是一种广泛使用的编程语言,它的语法规则和结构为编程提供了清晰的框架。为了编写出易于理解和维护的代码,必须掌握Java的基本语法元素,包括注释、标识符和关键字。本文档将详细介绍这些概念,并提供示例以帮助初学者了解和应用。
世间万物皆对象
2024/10/22
1820
第2章 Kotlin 语法基础第2章 Kotlin 语法基础
人与人之间通过语言来交流沟通,互相协作。人与计算机之间怎样“交流沟通”呢?答案是编程语言。一门语言有词、短语、句子、文章等,对应到编程语言中就是关键字、标识符、表达式、源代码文件等。通常一门编程语言的基本构成如下图所示
一个会写诗的程序员
2018/08/17
2.8K0
第2章 Kotlin 语法基础第2章 Kotlin 语法基础
Java基础-Java基础-02总结关键字,标识符,注释,常量进制,变量数据类型
程序员:为什么选择Java? 1:关键字(掌握) (1)被Java语言赋予特定含义的单词 (2)特点: 全部小写。 (3)注意事项: A:goto和const作为保留字存在。 B:类似于N
Java帮帮
2018/03/15
8120
Java基础-Java基础-02总结关键字,标识符,注释,常量进制,变量数据类型
推荐阅读
相关推荐
Kotlin 语言中的“关键字” Keywords in Kotlin修饰符关键字
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验