C++ STL库复习(10)map

作为关联式容器的一种,map 容器存储的都是 pair 对象,也就是用 pair 类模板创建的键值对。其中,各个键值对的键和值可以是任意数据类型,包括 C++ 基本数据类型(int、double 等)、使用结构体或类自定义的类型。

与此同时,在使用 map 容器存储多个键值对时,该容器会自动根据各键值对的键的大小,按照既定的规则进行排序。默认情况下,map 容器选用std::less排序规则(其中 T 表示键的数据类型),其会根据键的大小对所有键值对做升序排序。当然,根据实际情况的需要,我们可以手动指定 map 容器的排序规则,既可以选用 STL 标准库中提供的其它排序规则(比如std::greater),也可以自定义排序规则。

另外需要注意的是,使用 map 容器存储的各个键值对,键的值既不能重复也不能被修改。换句话说,map 容器中存储的各个键值对不仅键的值独一无二,键的类型也会用 const 修饰,这意味着只要键值对被存储到 map 容器中,其键的值将不能再做任何修改。

map 容器定义在 < map > 头文件中,并位于 std 命名空间中。

创建 map 容器

1

通过调用 map 容器类的默认构造函数,可以创建出一个空的 map 容器,比如:

std::map<std::string, int> myMap;

通过此方式创建出的 myMap 容器,初始状态下是空的,即没有存储任何键值对。鉴于空 map 容器可以根据需要随时添加新的键值对,因此创建空 map 容器是比较常用的。

2

当然在创建 map 容器的同时,也可以进行初始化,比如:

std::map<std::string, int> myMap{ {"C语言",10},{"STL",20} };

由此,myMap 容器在初始状态下,就包含有 2 个键值对。
再次强调,map 容器中存储的键值对,其本质都是 pair 类模板创建的 pair 对象。因此,下面程序也可以创建出一模一样的 myMap 容器:

std::map<std::string, int> myMap{std::make_pair("C语言",10),std::make_pair("STL",20)};

3

除此之外,在某些场景中,可以利用先前已创建好的 map 容器,再创建一个新的 map 容器。例如:

std::map<std::string, int> newMap(myMap);

由此,通过调用 map 容器的拷贝(复制)构造函数,即可成功创建一个和 myMap 完全一样的 newMap 容器。

C++ 11 标准中,还为 map 容器增添了移动构造函数。当有临时的 map 对象作为参数,传递给要初始化的 map 容器时,此时就会调用移动构造函数。举个例子:

#创建一个会返回临时 map 对象的函数
std::map<std::string,int> disMap() {
    std::map<std::string, int> tempMap{ {"C语言",10},{"STL",20} };
    return tempMap;
}
//调用 map 类模板的移动构造函数创建 newMap 容器
std::map<std::string, int> newMap(disMap());

4

map 类模板还支持取已建 map 容器中指定区域内的键值对,创建并初始化新的 map 容器。例如:

std::map<std::string, int> myMap{ {"C语言",10},{"STL",20} };
std::map<std::string, int> newMap(++myMap.begin(), myMap.end());

这里,通过调用 map 容器的双向迭代器,实现了在创建 newMap 容器的同时,将其初始化为包含一个 {“STL”,20} 键值对的容器。

5

当然,在以上几种创建 map 容器的基础上,我们都可以手动修改 map 容器的排序规则。默认情况下,map 容器调用 std::less 规则,根据容器内各键值对的键的大小,对所有键值对做升序排序。

因此,如下 2 行创建 map 容器的方式,其实是等价的:

std::map<std::string, int> myMap{ {"C语言",10},{"STL",20} };
std::map<std::string, int, std::less<std::string>> myMap{ {"C语言",10},{"STL",20} };

以上两种创建方式生成的 myMap 容器,其内部键值对排列的顺序为:

<"C语言", 10>
<"STL", 20>

下面程序手动修改了 myMap 容器的排序规则,令其作升序排序:

std::map<std::string, int, std::greater<std::string>> myMap{ {"C语言",10},{"STL",20} };

此时,myMap 容器内部键值对排列的顺序为:

<"STL", 20>
<"C语言", 10>

下面,上一段代码:

#include <iostream>
#include <map>
#include <string>
using namespace std;

int main() 
{
    //创建空 map 容器,默认根据个键值对中键的值,对键值对做降序排序
    std::map<std::string, std::string, std::greater<std::string>> myMap;
    //调用 emplace() 方法,直接向 myMap 容器中指定位置构造新键值对
    myMap.emplace("C语言","111");
    myMap.emplace("Python", "222");
    myMap.emplace("STL", "333");
    //输出当前 myMap 容器存储键值对的个数
    std::cout << "myMap size == " << myMap.size() << std::endl;
    //判断当前 myMap 容器是否为空
    if (!myMap.empty()) 
    {
        //借助 myMap 容器迭代器,将该容器的键值对逐个输出
        for (auto i = myMap.begin(); i != myMap.end(); ++i) 
        {
            std::cout << i->first << " " << i->second << std::endl;
        }
    }  
    return 0;
}

输出结果为:

myMap size == 3
STL 333
Python 222
C语言 111

map容器迭代器

C++ STL 标准库为 map 容器配备的是双向迭代器(bidirectional iterator)。这意味着,map 容器迭代器只能进行 ++p、p++、–p、p–、*p 操作,并且迭代器之间只能使用 == 或者 != 运算符进行比较。

下面程序以 begin()/end() 组合为例,演示了如何遍历 map 容器:

#include <iostream>
#include <map>
#include <string>
 
using namespace std;

int main() 
{
    //创建并初始化 map 容器
    std::map<std::string, std::string>myMap{ {"STL","111"},{"C++","222"} };
    
    //调用 begin()/end() 组合,遍历 map 容器
    for (auto iter = myMap.begin(); iter != myMap.end(); ++iter) 
    {
        cout << iter->first << " " << iter->second << endl;
    }
    return 0;
}

输出结果为:

C++ 222
STL 111

除此之外,map 类模板中还提供了 find() 成员方法,它能帮我们查找指定 key 值的键值对,如果成功找到,则返回一个指向该键值对的双向迭代器;反之,其功能和 end() 方法相同。
举个例子:

#include <iostream>
#include <map>
#include <string>

using namespace std;

int main() 
{
    //创建并初始化 map 容器
    std::map<std::string, std::string> myMap{ {"STL","111"},
                                              {"C++","222"},
                                              {"Java","333"} };
    //查找键为 "Java" 的键值对
    auto iter = myMap.find("Java");
    //从 iter 开始,遍历 map 容器
    for (; iter != myMap.end(); ++iter) 
    {
        cout << iter->first << " " << iter->second << endl;
    }
    
    return 0;
}

输出结果:

Java 333
STL 111

同时,map 类模板中还提供有 lower_bound(key) 和 upper_bound(key) 成员方法,它们的功能是类似的,唯一的区别在于:

  • lower_bound(key) 返回的是指向第一个键不小于 key 的键值对的迭代器;
  • upper_bound(key) 返回的是指向第一个键大于 key 的键值对的迭代器;
#include <iostream>
#include <map>
#include <string>

using namespace std;

int main() 
{
    //创建并初始化 map 容器
    std::map<std::string, std::string>myMap{ {"STL","111"},
                                             {"C++","222"},
                                             {"Java","333"} };
                                             
    //找到第一个键的值不小于 "Java" 的键值对
    auto iter = myMap.lower_bound("Java");
    cout << "lower:" << iter->first << " " << iter->second << endl;
   
    //找到第一个键的值大于 "Java" 的键值对
    iter = myMap.upper_bound("Java");
    cout <<"upper:" << iter->first << " " << iter->second << endl;
    
    return 0;
}

输出结果:

lower:Java 333
upper:STL 111

equal_range(key) 成员方法可以看做是 lower_bound(key) 和 upper_bound(key) 的结合体,该方法会返回一个 pair 对象,其中的 2 个元素都是迭代器类型,其中 pair.first 实际上就是 lower_bound(key) 的返回值,而 pair.second 则等同于 upper_bound(key) 的返回值。

显然,equal_range(key) 成员方法表示的一个范围,位于此范围中的键值对,其键的值都为 key。举个例子:

#include <iostream>
#include <utility>
#include <map>
#include <string>

using namespace std;

int main() 
{
    //创建并初始化 map 容器
    std::map<string, string> myMap{ {"STL","111"},
                                    {"C++","222"},
                                    {"Java","333"} };
    //创建一个 pair 对象,来接收 equal_range() 的返回值
    std::pair<std::map<string, string>::iterator, std::map<string, string>::iterator> myPair = myMap.equal_range("C++");
    
    //通过遍历,输出 myPair 指定范围内的键值对
    for (auto iter = myPair.first; iter != myPair.second; ++iter) 
    {
        cout << iter->first << " " << iter->second << endl;
    }
    return 0;
}

输出结果:

C++ 222

和 lower_bound(key)、upper_bound(key) 一样,该方法也更常用于 multimap 容器,因为 map 容器中各键值对的键的值都是唯一的,因此通过 map 容器调用此方法,其返回的范围内最多也只有 1 个键值对。

map获取键对应值

1

map 类模板中对[ ]运算符进行了重载,这意味着,类似于借助数组下标可以直接访问数组中元素,通过指定的键,我们可以轻松获取 map 容器中该键对应的值。

举个例子:

#include <iostream>
#include <map>
#include <string>

using namespace std;

int main() 
{
    //创建并初始化 map 容器
    std::map<std::string, std::string>myMap{ {"STL","111"},
                                             {"C++","222"},
                                             {"Java","333"} };
    string cValue = myMap["C++"];
    cout << cValue << endl;
    
    return 0;
}

输出结果:

222

注意,只有当 map 容器中确实存有包含该指定键的键值对,借助重载的 [ ] 运算符才能成功获取该键对应的值;反之,若当前 map 容器中没有包含该指定键的键值对,则此时使用 [ ] 运算符将不再是访问容器中的元素,而变成了向该 map 容器中增添一个键值对。其中,该键值对的键用 [ ] 运算符中指定的键,其对应的值取决于 map 容器规定键值对中值的数据类型,如果是基本数据类型,则值为 0;如果是 string 类型,其值为 " ",即空字符串(即使用该类型的默认值作为键值对的值)。

举个例子:

#include <iostream>
#include <map>
#include <string>

using namespace std;

int main() 
{
    //创建空 map 容器
    std::map<std::string, int> myMap;
    int cValue = myMap["C++"];
    
    for (auto i = myMap.begin(); i != myMap.end(); ++i) 
    {
        cout << i->first << " "<< i->second << endl;
    }
    return 0;
}

输出结果:

C++ 0

显然,对于空的 myMap 容器来说,其内部没有以 “C++” 为键的键值对,这种情况下如果使用 [ ] 运算符获取该键对应的值,其功能就转变成了向该 myMap 容器中添加一个<“C++”,0>键值对(由于 myMap 容器规定各个键值对的值的类型为 int,该类型的默认值为 0)。

实际上,[ ] 运算符确实有 "为 map 容器添加新键值对” 的功能,但前提是要保证新添加键值对的键和当前 map 容器中已存储的键值对的键都不一样。例如:

#include <iostream>
#include <map>
#include <string>

using namespace std;

int main() 
{
    //创建空 map 容器
    std::map<string, string>myMap;
    
    myMap["STL"] = "111";
    cout << "STL " << myMap["STL"] << endl;
    
    myMap["Python"] = "222";
    myMap["STL"] = "333";
    
    for (auto i = myMap.begin(); i != myMap.end(); ++i) 
    {
        cout << i->first << " " << i->second << endl;
    }
    
    return 0;
}

输出结果:

STL 111
Python 222
STL 333

2

除了借助 [ ] 运算符获取 map 容器中指定键对应的值,还可以使用 at() 成员方法。和前一种方法相比,at() 成员方法也需要根据指定的键,才能从容器中找到该键对应的值;不同之处在于,如果在当前容器中查找失败,该方法不会向容器中添加新的键值对,而是直接抛出 out_of_range 异常。

举个例子:

#include <iostream>
#include <map>
#include <string>

using namespace std;

int main() 
{
    //创建并初始化 map 容器
    std::map<std::string, std::string> myMap{ {"STL","111"},
                                              {"C++","222"},
                                              {"Java","333"} };
    cout << myMap.at("C++") << endl;
    //下面一行代码会引发 out_of_range 异常
    //cout << myMap.at("Python") << endl;
    
    return 0;
}

输出结果:

222

除了可以直接获取指定键对应的值之外,还可以借助 find() 成员方法间接实现此目的。和以上 2 种方式不同的是,该方法返回的是一个迭代器,即如果查找成功,该迭代器指向查找到的键值对;反之,则指向 map 容器最后一个键值对之后的位置(和 end() 成功方法返回的迭代器一样)。

举个例子:

#include <iostream>
#include <map>
#include <string>

using namespace std;

int main() 
{
    //创建并初始化 map 容器
    std::map<std::string, std::string> myMap{ {"STL","111"},
                                              {"C++","222"},
                                              {"Java","333"} };
                                              
    map< std::string, std::string >::iterator myIter = myMap.find("C++");
    cout << myIter->first << " " << myIter->second << endl;
    
    return 0;
}

输出结果:

C++ 222

如果以上方法都不适用,我们还可以遍历整个 map 容器,找到包含指定键的键值对,进而获取该键对应的值。比如:

#include <iostream>
#include <map>
#include <string>

using namespace std;

int main() 
{
    //创建并初始化 map 容器
    std::map<std::string, std::string> myMap{ {"STL","111"},
                                              {"C++","222"},
                                              {"Java","333"} };
                                              
    for (auto iter = myMap.begin(); iter != myMap.end(); ++iter) 
    {
        //调用 string 类的 compare() 方法,找到一个键和指定字符串相同的键值对
        if (!iter->first.compare("C++")) 
        {
            cout << iter->first << " " << iter->second << endl;
        }
    }
    
    return 0;
}

输出结果:

C++ 222

map insert()插入数据的4种方式

1

需指定插入位置,直接将键值对添加到 map 容器中。insert() 方法的语法格式有以下 2 种:

//1、引用传递一个键值对
pair<iterator,bool> insert (const value_type& val);
//2、以右值引用的方式传递键值对
template <class P>
    pair<iterator,bool> insert (P&& val);

其中,val 参数表示键值对变量,同时该方法会返回一个 pair 对象,其中 pair.first 表示一个迭代器,pair.second 为一个 bool 类型变量:

  • 如果成功插入 val,则该迭代器指向新插入的 val,bool 值为 true;
  • 如果插入 val 失败,则表明当前 map 容器中存有和 val 的键相同的键值对(用 p 表示),此时返回的迭代器指向 p,bool值为 false。

举个例子:

#include <iostream>
#include <map>
#include <string>

using namespace std;

int main()
{
    //创建一个空 map 容器
    std::map<string, string> mymap;
   
    //创建一个真实存在的键值对变量
    std::pair<string, string> STL = { "STL","111" };
   
    //创建一个接收 insert() 方法返回值的 pair 对象
    std::pair<std::map<string, string>::iterator, bool> ret;
   
    //插入 STL,由于 STL 并不是临时变量,因此会以第一种方式传参
    ret = mymap.insert(STL);
    cout << "ret.iter = <{" << ret.first->first << ", " << ret.first->second << "}, " << ret.second << ">" << endl;
    
    //以右值引用的方式传递临时的键值对变量
    ret = mymap.insert({ "C++","222" });
    cout << "ret.iter = <{" << ret.first->first << ", " << ret.first->second << "}, " << ret.second << ">" << endl;
    
    //插入失败样例
    ret = mymap.insert({ "STL","333" });
    cout << "ret.iter = <{" << ret.first->first << ", " << ret.first->second << "}, " << ret.second << ">" << endl;
    
    return 0;
}

输出结果:

ret.iter = <{STL, 111}, 1>
ret.iter = <{C++, 222}, 1>
ret.iter = <{STL, 111}, 0>

从执行结果中不难看出,程序中共执行了 3 次插入操作,其中成功了 2 次,失败了 1 次:

  • 对于插入成功的 insert() 方法,其返回的 pair 对象中包含一个指向新插入键值对的迭代器和值为 1 的 bool 变量
  • 对于插入失败的 insert() 方法,同样会返回一个 pair 对象,其中包含一个指向 map 容器中键为 “STL” 的键值对和值为 0 的 bool 变量。

另外,还可以使用如下 2 种方式创建临时的键值对变量,它们是等价的:

//调用 pair 类模板的构造函数
ret = mymap.insert(pair<string,string>{ "C++","222" });
//调用 make_pair() 函数
ret = mymap.insert(make_pair("C++", "222"));

2

除此之外,insert() 方法还支持向 map 容器的指定位置插入新键值对,该方法的语法格式如下:

//以普通引用的方式传递 val 参数
iterator insert (const_iterator position, const value_type& val);
//以右值引用的方式传递 val 键值对参数
template <class P>
    iterator insert (const_iterator position, P&& val);

其中 val 为要插入的键值对变量。注意,和第 1 种方式的语法格式不同,这里 insert() 方法返回的是迭代器,而不再是 pair 对象:

  • 如果插入成功,insert() 方法会返回一个指向 map 容器中已插入键值对的迭代器;
  • 如果插入失败,insert() 方法同样会返回一个迭代器,该迭代器指向 map 容器中和 val 具有相同键的那个键值对。

举个例子:

#include <iostream>
#include <map>
#include <string>

using namespace std;

int main()
{
    //创建一个空 map 容器
    std::map<string, string> mymap;
   
    //创建一个真实存在的键值对变量
    std::pair<string, string> STL = { "STL","111" };
    //指定要插入的位置
    std::map<string, string>::iterator it = mymap.begin();
    
    //向 it 位置以普通引用的方式插入 STL
    auto iter1 = mymap.insert(it, STL);
    cout << iter1->first << " " << iter1->second << endl;
    //向 it 位置以右值引用的方式插入临时键值对
    auto iter2 = mymap.insert(it, std::pair<string, string>("C++", "222"));
    cout << iter2->first << " " << iter2->second << endl;
    //插入失败样例
    auto iter3 = mymap.insert(it, std::pair<string, string>("STL", "333"));
    cout << iter3->first << " " << iter3->second << endl;
    
    return 0;
}

输出结果:

STL 111
C++ 222
STL 111

3

insert() 方法还支持向当前 map 容器中插入其它 map 容器指定区域内的所有键值对,该方法的语法格式如下:

template <class InputIterator>
  void insert (InputIterator first, InputIterator last);

其中 first 和 last 都是迭代器,它们的组合<first,last>可以表示某 map 容器中的指定区域。

举个例子:

#include <iostream>
#include <map>
#include <string>

using namespace std;

int main()
{
    //创建并初始化 map 容器
    std::map<std::string, std::string> mymap{ {"STL","111"},
                                              {"C++","222"},
                                              {"Java","333"} };
    //创建一个空 map 容器
    std::map<std::string, std::string> copymap;
    //指定插入区域
    std::map<string, string>::iterator first = ++mymap.begin();
    std::map<string, string>::iterator last = mymap.end();
    //将<first,last>区域内的键值对插入到 copymap 中
    copymap.insert(first, last);
    
    //遍历输出 copymap 容器中的键值对
    for (auto iter = copymap.begin(); iter != copymap.end(); ++iter) 
    {
        cout << iter->first << " " << iter->second << endl;
    }
    
    return 0;
}

输出结果:

Java 333
STL 111

此程序中,<first,last> 指定的区域是从 mymap 容器第 2 个键值对开始,之后所有的键值对,所以 copymap 容器中包含有 2 个键值对。

4

除了以上一种格式外,insert() 方法还允许一次向 map 容器中插入多个键值对,其语法格式为:

void insert ({val1, val2, ...});

其中,vali 都表示的是键值对变量。

举个例子:

#include <iostream>
#include <map>
#include <string>

using namespace std;

int main()
{
    //创建空的 map 容器
    std::map<std::string, std::string> mymap;
    //向 mymap 容器中添加 3 个键值对
    mymap.insert({ {"STL", "111"},
                   {"C++","222"},
                   {"Java","333"} });
                   
    for (auto iter = mymap.begin(); iter != mymap.end(); ++iter) 
    {
        cout << iter->first << " " << iter->second << endl;
    }
    
    return 0;
}

输出结果:

C++ 222
Java 333
STL 111

map emplace()和emplace_hint()

1

和 insert() 方法相比,emplace() 和 emplace_hint() 方法的使用要简单很多,因为它们各自只有一种语法格式。其中,emplace() 方法的语法格式如下:

template <class... Args>
  pair<iterator,bool> emplace (Args&&... args);

参数 (Args&&… args) 指的是,这里只需要将创建新键值对所需的数据作为参数直接传入即可,此方法可以自行利用这些数据构建出指定的键值对。
另外,该方法的返回值也是一个 pair 对象,其中 pair.first 为一个迭代器,pair.second 为一个 bool 类型变量:

  • 当该方法将键值对成功插入到 map 容器中时,其返回的迭代器指向该新插入的键值对,同时 bool 变量的值为 true;
  • 当插入失败时,则表明 map 容器中存在具有相同键的键值对,此时返回的迭代器指向此具有相同键的键值对,同时 bool 变量的值为false。

下面程序演示 emplace() 方法的具体用法:

#include <iostream>
#include <map>
#include <string>

using namespace std;

int main()
{
    //创建并初始化 map 容器
    std::map<string, string>mymap;
    
    //插入键值对
    pair<map<string, string>::iterator, bool> ret = mymap.emplace("STL", "111");
    cout << "1、ret.iter = <{" << ret.first->first << ", " << ret.first->second << "}, " << ret.second << ">" << endl;
    //插入新键值对
    ret = mymap.emplace("C++", "222");
    cout << "2、ret.iter = <{" << ret.first->first << ", " << ret.first->second << "}, " << ret.second << ">" << endl;
    //失败插入的样例
    ret = mymap.emplace("STL", "333");
    cout << "3、ret.iter = <{" << ret.first->first << ", " << ret.first->second << "}, " << ret.second << ">" << endl;
    
    return 0;
}

输出结果:

1、ret.iter = <{STL, 111}, 1>
2、ret.iter = <{C++, 222}, 1>
3、ret.iter = <{STL, 111}, 0>

可以看到,程序中共执行了 3 次向 map 容器插入键值对的操作,其中前 2 次都成功了,第 3 次由于要插入的键值对的键和 map 容器中已存在的键值对的键相同,因此插入失败。

2

emplace_hint() 方法的功能和 emplace() 类似,其语法格式如下:

template <class... Args>
  iterator emplace_hint (const_iterator position, Args&&... args);

显然和 emplace() 语法格式相比,有以下 2 点不同:

  • 该方法不仅要传入创建键值对所需要的数据,还需要传入一个迭代器作为第一个参数,指明要插入的位置(新键值对键会插入到该迭代器指向的键值对的前面);
  • 该方法的返回值是一个迭代器,而不再是 pair 对象。当成功插入新键值对时,返回的迭代器指向新插入的键值对;反之,如果插入失败,则表明map 容器中存有相同键的键值对,返回的迭代器就指向这个键值对。

下面程序演示 emplace_hint() 方法的用法:

#include <iostream>
#include <map>
#include <string>

using namespace std;

int main()
{
    //创建并初始化 map 容器
    std::map<string, string>mymap;
    
    //指定在 map 容器插入键值对
    map<string, string>::iterator iter = mymap.emplace_hint(mymap.begin(),"STL", "111");
    cout << iter->first << " " << iter->second << endl;
    iter = mymap.emplace_hint(mymap.begin(), "C++", "222");
    cout << iter->first << " " << iter->second << endl;
    //插入失败样例
    iter = mymap.emplace_hint(mymap.begin(), "STL", "333");
    cout << iter->first << " " << iter->second << endl;
    
    return 0;
}

输出结果:

STL 111
C++ 222
STL 111

注意,和 insert() 方法一样,虽然 emplace_hint() 方法指定了插入键值对的位置,但 map 容器为了保持存储键值对的有序状态,可能会移动其位置。

创建C++ multimap容器的方法

multimap 容器具有和 map 相同的特性,即 multimap 容器也用于存储 pair<const K, T> 类型的键值对(其中 K 表示键的类型,T 表示值的类型),其中各个键值对的键的值不能做修改;并且,该容器也会自行根据键的大小对存储的所有键值对做排序操作。和 map 容器的区别在于,multimap 容器中可以同时存储多(≥2)个键相同的键值对。

和 map 容器一样,实现 multimap 容器的类模板也定义在< map >头文件,并位于 std 命名空间中。因此,在使用 multimap 容器前,程序应包含如下代码:

#include <map>
using namespace std;

multimap 容器类模板的定义如下:

template < class Key,                                   // 指定键(key)的类型
           class T,                                     // 指定值(value)的类型
           class Compare = less<Key>,                   // 指定排序规则
           class Alloc = allocator<pair<const Key,T> >  // 指定分配器对象的类型
           > class multimap;

可以看到,multimap 容器模板有 4 个参数,其中后 2 个参数都设有默认值。

multimap 类模板内部提供有多个构造函数,总的来说,创建 multimap 容器的方式可归为以下 5 种。

1

通过调用 multimap 类模板的默认构造函数,可以创建一个空的 multimap 容器:

std::multimap<std::string, std::string> mymultimap;

2

在创建 multimap 容器的同时,还可以进行初始化操作。比如:
//创建并初始化 multimap 容器

multimap<string, string> mymultimap{  {"C++", "111"},
                                      {"Python", "222"},
                                      {"STL", "333"}  };

注意,使用此方式初始化 multimap 容器时,其底层会先将每一个{key, value}创建成 pair 类型的键值对,然后再用已建好的各个键值对初始化 multimap 容器。

实际上,我们完全可以先手动创建好键值对,然后再用其初始化 multimap 容器。下面程序使用了 2 种方式创建 pair 类型键值对,再用其初始化 multimap 容器,它们是完全等价的:

//借助 pair 类模板的构造函数来生成各个pair类型的键值对
multimap<string, string> mymultimap{
    pair<string,string>{"C++", "111"},
    pair<string,string>{"Python", "222"},
    pair<string,string>{"STL", "333"}
};
//调用 make_pair() 函数,生成键值对元素
//创建并初始化 multimap 容器
multimap<string, string> mymultimap{
    make_pair{"C++", "111"},
    make_pair{"Python", "222"},
    make_pair{"STL", "333"}
};

3

除此之外,通过调用 multimap 类模板的拷贝(复制)构造函数,也可以初始化新的 multimap 容器。例如:

multimap<string, string> newmultimap(mymultimap);

由此,就成功创建一个和 mymultimap 完全一样的 newmultimap 容器。
在 C++ 11 标准中,还为 multimap 类增添了移动构造函数。即当有临时的 multimap 容器作为参数初始化新 multimap 容器时,其底层就会调用移动构造函数来实现初始化操作。举个例子:

//创建一个会返回临时 multimap 对象的函数
multimap<string, string> dismultimap() {
    multimap<string, string>tempmultimap{ {"C++", "111"},{"Python", "222"} };
    return tempmultimap;
}  
//调用 multimap 类模板的移动构造函数创建 newMultimap 容器
multimap<string, string> newmultimap(dismultimap());

上面程序中,由于 dismultimap() 函数返回的 tempmultimap 容器是一个临时对象,因此在实现初始化 newmultimap 容器时,底层调用的是 multimap 容器的移动构造函数,而不再是拷贝构造函数。

4

multimap 类模板还支持从已有 multimap 容器中,选定某块区域内的所有键值对,用作初始化新 multimap 容器时使用。例如:

//创建并初始化 multimap 容器
multimap<string, string> mymultimap{ {"C++", "111"},
                                     {"Python", "222"},
                                     {"STL", "333"} };
multimap<string, string> newmultimap(++mymultimap.begin(), mymultimap.end());

这里使用了 multimap 容器的迭代器,选取了 mymultimap 容器中的最后 2 个键值对,用于初始化 newmultimap 容器。

5

前面讲到,multimap 类模板共可以接收 4 个参数,其中第 3 个参数可用来修改 multimap 容器内部的排序规则。默认情况下,此参数的值为std::less,这意味着以下 2 种创建 multimap 容器的方式是等价的:

multimap<char, int> mymultimap{ {'a',1},{'b',2} };
multimap<char, int, std::less<char>> mymultimap{ {'a',1},{'b',2} };

和 map 容器相比,multimap 未提供 at() 成员方法,也没有重载 [] 运算符。
这意味着,map 容器中通过指定键获取指定指定键值对的方式,将不再适用于 multimap 容器。其实这很好理解,因为 multimap 容器中指定的键可能对应多个键值对,而不再是 1 个。
举个例子:

#include <iostream>
#include <map> 

using namespace std;   

int main()
{
    //创建并初始化 multimap 容器
    multimap<char, int>mymultimap{ {'a',10},{'b',20},{'b',15}, {'c',30} };
    //输出 mymultimap 容器存储键值对的数量
    cout << mymultimap.size() << endl;
    //输出 mymultimap 容器中存储键为 'b' 的键值对的数量
    cout << mymultimap.count('b') << endl;
    
    for (auto iter = mymultimap.begin(); iter != mymultimap.end(); ++iter) 
    {
        cout << iter->first << " " << iter->second << endl;
    }
    
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值