C++STL: 連想コンテナのマップとマルチマップ

1.地図

概要

マップ コンテナーは、一種の連想コンテナーとして、ペア オブジェクト、つまりペア クラス テンプレートで作成されたキーと値のペアを格納します。このうち、各キーと値のペアのキーと値は、C++ の基本データ型 (int、double など)、構造体、クラス定義型など、任意のデータ型にすることができます。

通常、マップ コンテナーに格納される各キーと値のペアは、キー タイプとして文字列 string を使用します。

同時に、マップ コンテナーを使用して複数のキーと値のペアを保存すると、コンテナーは各キーと値のペアのキーのサイズに応じて、確立されたルールに従って自動的に並べ替えます。デフォルトでは、マップ コンテナーはstd::less<T>照合順序 (T はキーのデータ型を表します) を使用し、すべてのキーと値のペアをキーのサイズに従って昇順に並べ替えます。もちろん、実際の状況のニーズに応じて、マップ コンテナのソート ルールを手動で指定することもでき、STL 標準ライブラリで提供されている他のソート ルール (たとえば) を選択したり、ソート ルールをカスタマイズしたりすることもできますstd::greater<T>

さらに、マップ コンテナに格納されているキーと値のペアごとに、キーの値を繰り返したり変更したりすることはできないことに注意してください。つまり、マップ コンテナーに格納されている各キーと値のペアは一意であるだけでなく、キーの型も const で変更されます。つまり、キーと値のペアがマップ コンテナーに格納されている限り、キーの値は変更されなくなります。

前述したように、マップ コンテナーは、ペア型のキーと値のペア要素を格納します。より正確には、コンテナーは、pair<const K, T> 型を格納します (K と T はそれぞれキーと値のデータ型を表します)。キーと値のペアの要素。

マップ コンテナは <map> ヘッダー ファイルで定義され、std 名前空間に存在します。したがって、マップ コンテナーを使用する場合は、コードに次のステートメントを含める必要があります。

#include <map>
using namespace std;

コードの 2 行目は必要ないことに注意してください。必要ない場合は、後続のプログラムでマップ コンテナーを使用するときに std 名前空間を手動で指定する必要があります (初心者に推奨)。

マップ コンテナのテンプレート定義は次のとおりです。

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

マップ コンテナー テンプレートには 4 つのパラメーターがあり、最後の 2 つのパラメーターにはデフォルト値があることがわかります。ほとんどのシナリオでは、最初の 2 つのパラメーターの値を設定するだけで済みます。一部のシナリオでは 3 番目のパラメーターを使用する場合がありますが、最後のパラメーターはほとんど使用されません。

メンバー関数

次の表に、マップ コンテナーによって提供される共通のメンバー メソッドとそれぞれの関数を示します。

メンバー関数 関数
始める() コンテナ内の最初の (最初にソートされたことに注意してください) キーと値のペアを指す双方向イテレータを返します。マップ コンテナが const 修飾されている場合、このメソッドは const 型の双方向イテレータを返します。
終わり() コンテナの最後の要素 (ソートされた最後の要素であることに注意してください) の後の位置を指す双方向イテレータを返します。通常は begin() と組み合わせて使用​​されます。マップ コンテナが const 修飾されている場合、このメソッドは const 型の双方向イテレータを返します。
rbegin() 最後の (注、最後にソートされた) 要素を指す逆双方向反復子を返します。マップ コンテナが const 修飾されている場合、このメソッドは const 型の逆双方向反復子を返します。
レンド() 最初の (最初にソートされたことに注意してください) 要素の位置より前の位置を指す逆双方向反復子を返します。マップ コンテナが const 修飾されている場合、このメソッドは const 型の逆双方向反復子を返します。
cbegin() begin() と同じ機能がありますが、これに基づいて const 属性が追加されており、コンテナに格納されているキーと値のペアを変更するために使用することはできません。
いくつか() これは end() と同じ機能を持ちますが、その先頭に const 属性が追加される点が異なります。const 属性はコンテナに格納されているキーと値のペアの変更には使用できません。
crbegin() これは rbegin() と同じ機能を持っていますが、これに基づいて const 属性が追加されており、コンテナに格納されているキーと値のペアを変更するために使用することはできません。
クレンド() これは、 rend() と同じ機能を持ちますが、それに基づいて const 属性が追加される点が異なります。この属性は、コンテナーに格納されているキーと値のペアの変更には使用できません。
検索(キー) マップ コンテナーでキーが key であるキーと値のペアを検索します。正常に見つかった場合は、そのキーと値のペアを指す双方向反復子を返します。それ以外の場合は、end() メソッドと同じ反復子を返します。さらに、マップ コンテナが const 修飾されている場合、このメソッドは const 型の双方向イテレータを返します。
lower_bound(キー) 現在のマップ コンテナ内のキー以上の最初のキーと値のペアを指す双方向イテレータを返します。マップ コンテナが const 修飾されている場合、このメソッドは const 型の双方向イテレータを返します。
上限(キー) 現在のマップ コンテナ内のキーより大きい最初のキーと値のペアを指す反復子を返します。マップ コンテナが const 修飾されている場合、このメソッドは const 型の双方向イテレータを返します。
等しい範囲(キー) このメソッドはペア オブジェクト (2 つの双方向反復子を含む) を返します。ここで、pair.first メソッドと lower_bound() メソッドの戻り値は同等であり、pair.second メソッドと upper_bound() メソッドの戻り値は同等です。つまり、このメソッドは、キーが key であるキーと値のペアを含む範囲を返します (マップ コンテナーのキーと値のペアは一意であるため、範囲には最大 1 つのキーと値のペアが含まれます)。
空の() コンテナが空の場合は true を返し、それ以外の場合は false を返します。
サイズ() 現在のマップ コンテナに格納されているキーと値のペアの数を返します。
max_size() マップ コンテナが保持できるキーと値のペアの最大数を返します。オペレーティング システムが異なると、戻り値も異なります。
オペレーター[] マップ コンテナーは [] 演算子をオーバーロードします。マップ コンテナー内のキーと値のペアのキーの値がわかっている限り、配列内の要素を取得するのと同じように、キーを介して対応する値を直接取得できます。
at(キー) マップ コンテナ内のキー key に対応する値を見つけます。見つからない場合、関数は範囲外例外を発生させます。
入れる() キーと値のペアをマップ コンテナーに挿入します。
消去() マップ コンテナーの指定された領域内の指定された場所、指定されたキー (キー) 値、またはキーと値のペアを削除します。後続の章では、この方法に焦点を当てます。
スワップ() 2 つのマップ コンテナーに格納されているキーと値のペアを交換します。これは、操作の 2 つのキーと値のペアの型が同じである必要があることを意味します。
クリア() マップ コンテナーの size() が 0 であっても、マップ コンテナー内のすべてのキーと値のペアをクリアします。
場所() 現在のマップ コンテナー内の指定された位置に新しいキーと値のペアを構築します。その効果はキーと値のペアの挿入と同じですが、より効率的です。
emplace_hint() これは、emplace() がマップ コンテナー内で新しいキーと値のペアを構築する方法と本質的に同じですが、ユーザーがキーと値のペアが生成される場所を示す反復子をこのメソッドに提供し、それを使用する必要がある点が異なります。メソッドの最初のパラメータとして。
カウント(キー) 現在のマップ コンテナーで、キーが key であるキーと値のペアの数を見つけて返します。マップ コンテナ内の各キーと値のペアのキー値は一意であるため、この関数の最大戻り値は 1 であることに注意してください。

C++ マップ コンテナーを作成するいくつかの方法

マップ コンテナのテンプレート クラスには複数のコンストラクタが含まれるため、マップ コンテナを作成するにはさまざまな方法があります。次に、マップ コンテナを作成するためによく使用されるいくつかのメソッドを紹介します。

  1. 空のマップ コンテナは、マップ コンテナ クラスのデフォルトのコンストラクターを呼び出すことで作成できます。たとえば、次のようになります。
std::map<std::string, int> myMap;

この方法で作成された myMap コンテナは、最初は空です。つまり、キーと値のペアは格納されていません。空のマップ コンテナーは必要に応じていつでも新しいキーと値のペアを追加できるため、空のマップ コンテナーを作成する方が一般的です。

  1. もちろん、マップ コンテナーの作成中に、次のように初期化することもできます。
std::map<std::string, int> myMap{ {"C语言教程",10},{"STL教程",20} };

したがって、myMap コンテナには初期状態で 2 つのキーと値のペアが含まれています。

繰り返しになりますが、マップ コンテナーに格納されているキーと値のペアは、本質的にはペア クラス テンプレートによって作成されたペア オブジェクトです。したがって、次のプログラムでもまったく同じ myMap コンテナを作成できます。

std::map<std::string, int> myMap{std::make_pair("C语言教程",10),std::make_pair("STL教程",20)};
  1. さらに、一部のシナリオでは、以前に作成したマップ コンテナーを使用して新しいマップ コンテナーを作成できます。例えば:
std::map<std::string, int> newMap(myMap);

したがって、マップ コンテナのコピー (レプリケーション) コンストラクターを呼び出すことで、myMap とまったく同じ newMap コンテナを正常に作成できます。

C++11 標準では、移動コンストラクターもマップ コンテナーに追加されます。移動コンストラクターは、一時マップ オブジェクトが初期化されるマップ コンテナーにパラメーターとして渡されるときに呼び出されます。例えば:

// 创建一个会返回临时 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());

コピー コンストラクターを呼び出す場合でも、コピー コンストラクターを呼び出す場合でも、2 つのコンテナーの型がまったく同じであることを確認する必要があることに注意してください。

  1. マップ クラス テンプレートは、確立されたマップ コンテナーの指定された領域でキーと値のペアを取得し、新しいマップ コンテナーを作成して初期化することもサポートします。例えば:
std::map<std::string, int> myMap{ {"C语言教程",10},{"STL教程",20} };
std::map<std::string, int> newMap(++myMap.begin(), myMap.end());

ここで、マップ コンテナの双方向イテレータを呼び出すことにより、newMap コンテナの作成中に、{"STL Tutorial",20} キーと値のペアを含むコンテナとして初期化されることがわかります。

  1. もちろん、上記のマップ コンテナーの作成に基づいて、マップ コンテナーの並べ替えルールを手動で変更できます。デフォルトでは、マップ コンテナーは std::less<T> ルールを呼び出し、コンテナー内の各キーと値のペアのキー サイズに従って、すべてのキーと値のペアを昇順で並べ替えます。したがって、マップ コンテナーを作成する次の 2 行は実際には同等です。
std::map<std::string, int> myMap{ {"C语言教程",10},{"STL教程",20} };
std::map<std::string, int, std::less<std::string> > myMap{ {"C语言教程",10},{"STL教程",20} };

上記の 2 つの作成方法で生成された 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>

一部の特定のシナリオでは、マップ コンテナーの並べ替えルールをカスタマイズする必要もあります。

イテレータ

先ほど学習した逐次コンテナでも連想コンテナでも、トラバーサル操作を実装するにはこのタイプのコンテナのイテレータを使用する必要があります。もちろん、マップ コンテナも例外ではありません。

C++ STL 標準ライブラリは、マップ コンテナに双方向イテレータを備えています。これは、マップ コンテナーの反復子は++p、p++、--p、p--、*p操作のみが可能であり、反復子は==or演算子を使用してのみ!=比較できることを意味します。

シーケンシャル コンテナと比較して、マップ コンテナは (以下の表に示すように) より多くのメンバー メソッドを提供し、それらを呼び出すことで、指定された意味を持つイテレータを簡単に取得できることに言及する価値があります。

メンバーメソッド 関数
始める() コンテナ内の最初の (最初にソートされたことに注意してください) キーと値のペアを指す双方向イテレータを返します。マップ コンテナが const 修飾されている場合、このメソッドは const 型の双方向イテレータを返します。
終わり() コンテナの最後の要素 (ソートされた最後の要素であることに注意してください) の後の位置を指す双方向イテレータを返します。通常は begin() と組み合わせて使用​​されます。マップ コンテナが const 修飾されている場合、このメソッドは const 型の双方向イテレータを返します。
rbegin() 最後の (注、最後にソートされた) 要素を指す逆双方向反復子を返します。マップ コンテナが const 修飾されている場合、このメソッドは const 型の逆双方向反復子を返します。
レンド() 最初の (最初にソートされたことに注意してください) 要素の位置より前の位置を指す逆双方向反復子を返します。マップ コンテナが const 修飾されている場合、このメソッドは const 型の逆双方向反復子を返します。
cbegin() begin() と同じ機能がありますが、これに基づいて const 属性が追加されており、コンテナに格納されているキーと値のペアを変更するために使用することはできません。
いくつか() これは end() と同じ機能を持ちますが、その先頭に const 属性が追加される点が異なります。const 属性はコンテナに格納されているキーと値のペアの変更には使用できません。
crbegin() これは rbegin() と同じ機能を持っていますが、これに基づいて const 属性が追加されており、コンテナに格納されているキーと値のペアを変更するために使用することはできません。
クレンド() これは、 rend() と同じ機能を持ちますが、それに基づいて const 属性が追加される点が異なります。この属性は、コンテナーに格納されているキーと値のペアの変更には使用できません。
検索(キー) マップ コンテナーでキーが key であるキーと値のペアを検索します。正常に見つかった場合は、そのキーと値のペアを指す双方向反復子を返します。それ以外の場合は、end() メソッドと同じ反復子を返します。さらに、マップ コンテナが const 修飾されている場合、このメソッドは const 型の双方向イテレータを返します。
lower_bound(キー) 現在のマップ コンテナ内のキー以上の最初のキーと値のペアを指す双方向イテレータを返します。マップ コンテナが const 修飾されている場合、このメソッドは const 型の双方向イテレータを返します。
上限(キー) 現在のマップ コンテナ内のキーより大きい最初のキーと値のペアを指す反復子を返します。マップ コンテナが const 修飾されている場合、このメソッドは const 型の双方向イテレータを返します。
equal_range(key) 该方法返回一个 pair 对象(包含 2 个双向迭代器),其中 pair.first 和 lower_bound() 方法的返回值等价,pair.second 和 upper_bound() 方法的返回值等价。也就是说,该方法将返回一个范围,该范围中包含的键为 key 的键值对(map 容器键值对唯一,因此该范围最多包含一个键值对)。

上表中多数的成员方法,诸如 begin()、end() 等,在学习顺序容器时已经多次使用过,它们的功能如下图所示:

在这里插入图片描述

注意,图中 Ei 表示的是 pair 类对象,即键值对。对于 map 容器来说,每个键值对的键的值都必须保证是唯一的。

  1. 下面程序以 begin()/end() 组合为例,演示如何遍历 map 容器:
#include <iostream>
#include <map>      // pair
#include <string>       // string
using namespace std;

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

程序执行结果为:

C语言教程 http://c.biancheng.net/c/
STL教程 http://c.biancheng.net/stl/
  1. 除此之外,map 类模板中还提供了 find() 成员方法,它能帮我们查找指定 key 值的键值对,如果成功找到,则返回一个指向该键值对的双向迭代器;反之,其功能和 end() 方法相同。举个例子:
#include <iostream>
#include <map>      // pair
#include <string>       // string
using namespace std;

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

程序执行结果为:

Java教程 http://c.biancheng.net/java/
STL教程 http://c.biancheng.net/stl/

此程序中,创建并初始化的 myMap 容器,默认会根据各键值对中键的值,对各键值对做升序排序,其排序的结果为:

<"C语言教程","http://c.biancheng.net/c/">
<"Java教程","http://c.biancheng.net/java/">
<"STL教程","http://c.biancheng.net/stl/">

在此基础上,通过调用 find() 方法,我们可以得到一个指向键为 “Java教程” 的键值对的迭代器,由此当使用 for 循环从该迭代器出开始遍历时,就只会遍历到最后 2 个键值对。

  1. 同时,map 类模板中还提供有 lower_bound(key) 和 upper_bound(key) 成员方法,它们的功能是类似的,唯一的区别在于:
  • lower_bound(key) 返回的是指向第一个键不小于 key 的键值对的迭代器;
  • upper_bound(key) 返回的是指向第一个键大于 key 的键值对的迭代器;

下面程序演示了它们的功能:

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

int main() {
    // 创建并初始化 map 容器
    std::map<std::string, std::string>myMap{ {"STL教程","http://c.biancheng.net/stl/"},
                                             {"C语言教程","http://c.biancheng.net/c/"},
                                             {"Java教程","http://c.biancheng.net/java/"} };
    // 找到第一个键的值不小于 "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教程 http://c.biancheng.net/java/
upper:STL教程 http://c.biancheng.net/stl/

lower_bound(key) 和 upper_bound(key) 更多用于 multimap 容器,在 map 容器中很少用到。

  1. 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>  // pair
#include <map>      // map
#include <string>       // string
using namespace std;

int main() {
    // 创建并初始化 map 容器
    std::map<string, string>myMap{ {"STL教程","http://c.biancheng.net/stl/"},
                                   {"C语言教程","http://c.biancheng.net/c/"},
                                   {"Java教程","http://c.biancheng.net/java/"} };
    // 创建一个 pair 对象,来接收 equal_range() 的返回值
    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语言教程 http://c.biancheng.net/c/

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

map获取键对应值的几种方法

我们知道,map 容器中存储的都是 pair 类型的键值对,但几乎在所有使用 map 容器的场景中,经常要做的不是找到指定的 pair 对象(键值对),而是从该容器中找到某个键对应的值。

注意,使用 map 容器存储的各个键值对,其键的值都是唯一的,因此指定键对应的值最多有 1 个。

庆幸的是,map 容器的类模板中提供了以下 2 种方法,可直接获取 map 容器指定键对应的值。

  1. map 类模板中对[]运算符进行了重载,这意味着,类似于借助数组下标可以直接访问数组中元素,通过指定的键,我们可以轻松获取 map 容器中该键对应的值。举个例子:
#include <iostream>
#include <map>      // map
#include <string>   // string
using namespace std;

int main() {
    // 创建并初始化 map 容器
    std::map<std::string, std::string>myMap{ {"STL教程","http://c.biancheng.net/stl/"},
                                             {"C语言教程","http://c.biancheng.net/c/"},
                                             {"Java教程","http://c.biancheng.net/java/"} };
    string cValue = myMap["C语言教程"];
    cout << cValue << endl;
    return 0;
}

程序执行结果为:

http://c.biancheng.net/c/

可以看到,在第 11 行代码中,通过指定键的值为 “C语言教程”,借助重载的 [ ] 运算符,就可以在 myMap 容器中直接找到该键对应的值。

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

#include <iostream>
#include <map>      // map
#include <string>   // 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>      // map
#include <string>   // string
using namespace std;

int main() {
    // 创建空 map 容器
    std::map<string, string>myMap;
    myMap["STL教程"]="http://c.biancheng.net/java/";
    myMap["Python教程"] = "http://c.biancheng.net/python/";
    myMap["STL教程"] = "http://c.biancheng.net/stl/";
    for (auto i = myMap.begin(); i != myMap.end(); ++i) {
        cout << i->first << " " << i->second << endl;
    }
    return 0;
}

程序执行结果为:

Python教程 http://c.biancheng.net/python/
STL教程 http://c.biancheng.net/stl/

注意,程序中第 9 行代码已经为 map 容器添加了一个以 “STL教程” 作为键的键值对,则第 11 行代码的作用就变成了修改该键对应的值,而不再是为 map 容器添加新键值对。

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

int main() {
    // 创建并初始化 map 容器
    std::map<std::string, std::string>myMap{ {"STL教程","http://c.biancheng.net/stl/"},
                                             {"C语言教程","http://c.biancheng.net/c/"},
                                             {"Java教程","http://c.biancheng.net/java/"} };
    cout << myMap.at("C语言教程") << endl;
    // 下面一行代码会引发 out_of_range 异常
    // cout << myMap.at("Python教程") << endl;
    return 0;
}

程序执行结果为:

http://c.biancheng.net/c/

程序第 11 行代码处,通过 myMap 容器调用 at() 成员方法,可以成功找到键为 “C语言教程” 的键值对,并返回该键对应的值;而第 13 行代码,由于当前 myMap 容器中没有以 “Python教程” 为键的键值对,会导致 at() 成员方法查找失败,并抛出 out_of_range 异常。

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

int main() {
    // 创建并初始化 map 容器
    std::map<std::string, std::string>myMap{ {"STL教程","http://c.biancheng.net/stl/"},
                                             {"C语言教程","http://c.biancheng.net/c/"},
                                             {"Java教程","http://c.biancheng.net/java/"} };
    map< std::string, std::string >::iterator myIter = myMap.find("C语言教程");
    cout << myIter->first << " " << myIter->second << endl;
    return 0;
}

程序执行结果为:

C语言教程 http://c.biancheng.net/c/

注意,此程序中如果 find() 查找失败,会导致第 12 行代码运行出错。因为当 find() 方法查找失败时,其返回的迭代器指向的是容器中最后一个键值对之后的位置,即不指向任何有意义的键值对,也就没有所谓的 first 和 second 成员了。

  1. 如果以上方法都不适用,我们还可以遍历整个 map 容器,找到包含指定键的键值对,进而获取该键对应的值。比如:
#include <iostream>
#include <map>      // map
#include <string>   // string
using namespace std;

int main() {
    // 创建并初始化 map 容器
    std::map<std::string, std::string>myMap{ {"STL教程","http://c.biancheng.net/stl/"},
                                             {"C语言教程","http://c.biancheng.net/c/"},
                                             {"Java教程","http://c.biancheng.net/java/"} };
    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语言教程 http://c.biancheng.net/c/

本节所介绍的几种方法中,仅从“在 map 容器存储的键值对中,获取指定键对应的值”的角度出发,更推荐使用 at() 成员方法,因为该方法既简单又安全。

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

前面讲过,C++ STL map 类模板中对[]运算符进行了重载,即根据使用场景的不同,借助[]运算符可以实现不同的操作。举个例子:

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

int main()
{
    std::map<string, string> mymap{ {"STL教程","http://c.biancheng.net/java/"} };
    // 获取已存储键值对中,指定键对应的值
    cout << mymap["STL教程"] << endl;
    // 向 map 容器添加新键值对
    mymap["Python教程"] = "http://c.biancheng.net/python/";
    // 修改 map 容器已存储键值对中,指定键对应的值
    mymap["STL教程"] = "http://c.biancheng.net/stl/";
    for (auto iter = mymap.begin(); iter != mymap.end(); ++iter) {
        cout << iter->first << " " << iter->second << endl;
    }
   
    return 0;
}

程序执行结果为:

http://c.biancheng.net/java/
Python教程 http://c.biancheng.net/python/
STL教程 http://c.biancheng.net/stl/

可以看到,当操作对象为 map 容器中已存储的键值对时,则借助 [ ] 运算符,既可以获取指定键对应的值,还能对指定键对应的值进行修改;反之,若 map 容器内部没有存储以 [ ] 运算符内指定数据为键的键值对,则使用 [ ] 运算符会向当前 map 容器中添加一个新的键值对。

实际上,除了使用 [ ] 运算符实现向 map 容器中添加新键值对外,map 类模板中还提供有 insert() 成员方法,该方法专门用来向 map 容器中插入新的键值对。

注意,这里所谓的“插入”,指的是 insert() 方法可以将新的键值对插入到 map 容器中的指定位置,但这与 map 容器会自动对存储的键值对进行排序并不冲突。当使用 insert() 方法向 map 容器的指定位置插入新键值对时,其底层会先将新键值对插入到容器的指定位置,如果其破坏了 map 容器的有序性,该容器会对新键值对的位置进行调整。

自 C++ 11 标准后,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。

以上 2 种语法格式的区别在于传递参数的方式不同,即无论是局部定义的键值对变量还是全局定义的键值对变量,都采用普通引用传递的方式;而对于临时的键值对变量,则以右值引用的方式传参。

举个例子:

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

int main()
{
    // 创建一个空 map 容器
    std::map<string, string> mymap;
   
    // 创建一个真实存在的键值对变量
    std::pair<string, string> STL = { "STL教程","http://c.biancheng.net/stl/" };
   
    // 创建一个接收 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语言教程","http://c.biancheng.net/c/" });
    cout << "ret.iter = <{" << ret.first->first << ", " << ret.first->second << "}, " << ret.second << ">" << endl;
    
    // 插入失败样例
    ret = mymap.insert({ "STL教程","http://c.biancheng.net/java/" });
    cout << "ret.iter = <{" << ret.first->first << ", " << ret.first->second << "}, " << ret.second << ">" << endl;
    return 0;
}

程序执行结果为:

ret.iter = <{
    
    STL教程, http://c.biancheng.net/stl/}, 1>
ret.iter = <{
    
    C语言教程, http://c.biancheng.net/c/}, 1>
ret.iter = <{
    
    STL教程, http://c.biancheng.net/stl/}, 0>

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

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

另外,在程序中的第 22 行代码,还可以使用如下 2 种方式创建临时的键值对变量,它们是等价的:

// 调用 pair 类模板的构造函数
ret = mymap.insert(pair<string,string>{ "C语言教程","http://c.biancheng.net/c/" });
// 调用 make_pair() 函数
ret = mymap.insert(make_pair("C语言教程", "http://c.biancheng.net/c/"));
  1. 除此之外,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>  // map
#include <string> // string
using namespace std;

int main()
{
    // 创建一个空 map 容器
    std::map<string, string> mymap;
   
    // 创建一个真实存在的键值对变量
    std::pair<string, string> STL = { "STL教程","http://c.biancheng.net/stl/" };
    // 指定要插入的位置
    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语言教程", "http://c.biancheng.net/c/"));
    cout << iter2->first << " " << iter2->second << endl;
    
    // 插入失败样例
    auto iter3 = mymap.insert(it, std::pair<string, string>("STL教程", "http://c.biancheng.net/java/"));
    cout << iter3->first << " " << iter3->second << endl;
    return 0;
}

程序执行结果为:

STL教程 http://c.biancheng.net/stl/
C语言教程 http://c.biancheng.net/c/
STL教程 http://c.biancheng.net/stl/

再次强调,即便指定了新键值对的插入位置,map 容器仍会对存储的键值对进行排序。也可以说,决定新插入键值对位于 map 容器中位置的,不是 insert() 方法中传入的迭代器,而是新键值对中键的值。

  1. insert() 方法还支持向当前 map 容器中插入其它 map 容器指定区域内的所有键值对,该方法的语法格式如下:
template <class InputIterator>
 void insert (InputIterator first, InputIterator last);

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

举个例子:

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

int main()
{
    // 创建并初始化 map 容器
    std::map<std::string, std::string>mymap{ {"STL教程","http://c.biancheng.net/stl/"},
                                                {"C语言教程","http://c.biancheng.net/c/"},
                                                {"Java教程","http://c.biancheng.net/java/"} };
    // 创建一个空 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教程 http://c.biancheng.net/java/p
STL教程 http://c.biancheng.net/stl/

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

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

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

举个例子:

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

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

程序执行结果为:

C语言教程 http://c.biancheng.net/c/
Java教程 http://c.biancheng.net/java/
STL教程 http://c.biancheng.net/stl/

map emplace()和emplace_hint()方法

学习 map insert() 方法时提到,C++ STL map 类模板中还提供了 emplace() 和 emplace_hint() 成员函数,也可以实现向 map 容器中插入新的键值对。

值得一提的是,实现相同的插入操作,无论是用 emplace() 还是 emplace_hont(),都比 insert() 方法的效率高。

  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>  // map
#include <string> // string
using namespace std;

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

程序执行结果为:

1、ret.iter = <{
    
    STL教程, http://c.biancheng.net/stl/}, 1>
2、ret.iter = <{
    
    C语言教程, http://c.biancheng.net/c/}, 1>
3、ret.iter = <{
    
    STL教程, http://c.biancheng.net/stl/}, 0>

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

  1. emplace_hint() 方法的功能和 emplace() 类似,其语法格式如下:
template <class... Args>
 iterator emplace_hint (const_iterator position, Args&&... args);

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

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

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

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

int main()
{
    // 创建并初始化 map 容器
    std::map<string, string>mymap;
    // 指定在 map 容器插入键值对
    map<string, string>::iterator iter = mymap.emplace_hint(mymap.begin(),"STL教程", "http://c.biancheng.net/stl/");
    cout << iter->first << " " << iter->second << endl;
    iter = mymap.emplace_hint(mymap.begin(), "C语言教程", "http://c.biancheng.net/c/");
    cout << iter->first << " " << iter->second << endl;
    // 插入失败样例
    iter = mymap.emplace_hint(mymap.begin(), "STL教程", "http://c.biancheng.net/java/");
    cout << iter->first << " " << iter->second << endl;
    return 0;
}

程序执行结果为:

STL教程 http://c.biancheng.net/stl/
C语言教程 http://c.biancheng.net/c/
STL教程 http://c.biancheng.net/stl/

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


2. multimap

概述

在掌握 C++ STL map 容器的基础上,本节介绍一个和 map 相似的关联容器,即 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 个参数都设有默认值。大多数场景中,我们只需要设定前 2 个参数的值,有些场景可能会用到第 3 个参数,但最后一个参数几乎不会用到。

成员函数

成员方法 功能
begin() 返回指向容器中第一个(注意,是已排好序的第一个)键值对的双向迭代器。如果 multimap 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。
end() 返回指向容器最后一个元素(注意,是已排好序的最后一个)所在位置后一个位置的双向迭代器,通常和 begin() 结合使用。如果 multimap 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。
rbegin() 返回指向最后一个(注意,是已排好序的最后一个)元素的反向双向迭代器。如果 multimap 容器用 const 限定,则该方法返回的是 const 类型的反向双向迭代器。
rend() 返回指向第一个(注意,是已排好序的第一个)元素所在位置前一个位置的反向双向迭代器。如果 multimap 容器用 const 限定,则该方法返回的是 const 类型的反向双向迭代器。
cbegin() 和 begin() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改容器内存储的键值对。
cend() 和 end() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改容器内存储的键值对。
crbegin() 和 rbegin() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改容器内存储的键值对。
crend() 和 rend() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改容器内存储的键值对。
find(key) 在 multimap 容器中查找首个键为 key 的键值对,如果成功找到,则返回指向该键值对的双向迭代器;反之,则返回和 end() 方法一样的迭代器。另外,如果 multimap 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。
lower_bound(key) 返回一个指向当前 multimap 容器中第一个大于或等于 key 的键值对的双向迭代器。如果 multimap 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。
upper_bound(key) 返回一个指向当前 multimap 容器中第一个大于 key 的键值对的迭代器。如果 multimap 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。
equal_range(key) 该方法返回一个 pair 对象(包含 2 个双向迭代器),其中 pair.first 和 lower_bound() 方法的返回值等价,pair.second 和 upper_bound() 方法的返回值等价。也就是说,该方法将返回一个范围,该范围中包含的键为 key 的键值对。
empty() 若容器为空,则返回 true;否则 false。
size() 返回当前 multimap 容器中存有键值对的个数。
max_size() 返回 multimap 容器所能容纳键值对的最大个数,不同的操作系统,其返回值亦不相同。
insert() 向 multimap 容器中插入键值对。
erase() 删除 multimap 容器指定位置、指定键(key)值或者指定区域内的键值对。
swap() 交换 2 个 multimap 容器中存储的键值对,这意味着,操作的 2 个键值对的类型必须相同。
clear() 清空 multimap 容器中所有的键值对,使 multimap 容器的 size() 为 0。
emplace() 在当前 multimap 容器中的指定位置处构造新键值对。其效果和插入键值对一样,但效率更高。
emplace_hint() 在本质上和 emplace() 在 multimap 容器中构造新键值对的方式是一样的,不同之处在于,使用者必须为该方法提供一个指示键值对生成位置的迭代器,并作为该方法的第一个参数。
count(key) 在当前 multimap 容器中,查找键为 key 的键值对的个数并返回。

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

另外值的一提的是,由于 multimap 容器可存储多个具有相同键的键值对,因此上表中的 lower_bound()、upper_bound()、equal_range() 以及 count() 成员方法会经常用到。

创建C++ multimap容器的方法

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

  1. 通过调用 multimap 类模板的默认构造函数,可以创建一个空的 multimap 容器:
std::multimap<std::string, std::string> mymultimap;
  1. 当然,在创建 multimap 容器的同时,还可以进行初始化操作。比如:
// 创建并初始化 multimap 容器
multimap<string, string>mymultimap{ {"C语言教程", "http://c.biancheng.net/c/"},
                                    {"Python教程", "http://c.biancheng.net/python/"},
                                    {"STL教程", "http://c.biancheng.net/stl/"} };

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

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

// 借助 pair 类模板的构造函数来生成各个pair类型的键值对
multimap<string, string> mymultimap{
    pair<string,string>{"C语言教程", "http://c.biancheng.net/c/"},
    pair<string,string>{ "Python教程", "http://c.biancheng.net/python/"},
    pair<string,string>{ "STL教程", "http://c.biancheng.net/stl/"}
};
// 调用 make_pair() 函数,生成键值对元素
// 创建并初始化 multimap 容器
multimap<string, string> mymultimap{
    make_pair("C语言教程", "http://c.biancheng.net/c/"),
    make_pair("Python教程", "http://c.biancheng.net/python/"),
    make_pair("STL教程", "http://c.biancheng.net/stl/")
};
  1. 除此之外,通过调用 multimap 类模板的拷贝(复制)构造函数,也可以初始化新的 multimap 容器。例如:
multimap<string, string> newmultimap(mymultimap);

由此,就成功创建一个和 mymultimap 完全一样的 newmultimap 容器。

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

// 创建一个会返回临时 multimap 对象的函数
multimap<string, string> dismultimap() {
    multimap<string, string>tempmultimap{ {"C语言教程", "http://c.biancheng.net/c/"},{"Python教程", "http://c.biancheng.net/python/"} };
    return tempmultimap;
}  
// 调用 multimap 类模板的移动构造函数创建 newMultimap 容器
multimap<string, string> newmultimap(dismultimap());

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

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

  1. multimap 类模板还支持从已有 multimap 容器中,选定某块区域内的所有键值对,用作初始化新 multimap 容器时使用。例如:
// 创建并初始化 multimap 容器
multimap<string, string>mymultimap{ {"C语言教程", "http://c.biancheng.net/c/"},
                                    {"Python教程", "http://c.biancheng.net/python/"},
                                    {"STL教程", "http://c.biancheng.net/stl/"} };
multimap<string, string>newmultimap(++mymultimap.begin(), mymultimap.end());

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

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

mymultimap 容器中键值对的存储顺序为:

<a,1>
<b,2>

下面程序利用了 STL 模板库提供的std::greater<T>排序函数,实现令 multimap 容器对存储的键值对做降序排序:

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

其内部键值对的存储顺序为:

<b,2>
<a,1>

在某些特定场景中,我们还可以为 multimap 容器自定义排序规则。

おすすめ

転載: blog.csdn.net/crossoverpptx/article/details/131722643