c++11 标准模板(STL)(std::unordered_multimap)

Posted 繁星璀璨G

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了c++11 标准模板(STL)(std::unordered_multimap)相关的知识,希望对你有一定的参考价值。

定义于头文件 <unordered_map>
template<

    class Key,
    class T,
    class Hash = std::hash<Key>,
    class KeyEqual = std::equal_to<Key>,
    class Allocator = std::allocator< std::pair<const Key, T> >

> class unordered_multimap;
(1)(C++11 起)
namespace pmr

    template <class Key, class T,
              class Hash = std::hash<Key>,
              class Pred = std::equal_to<Key>>
    using unordered_multimap = std::unordered_multimap<Key, T, Hash, Pred,
                                   std::pmr::polymorphic_allocator<std::pair<const Key,T>>>;

(2)(C++17 起)

 

unordered_multimap 是无序关联容器,支持等价的关键(一个 unordered_multimap 可含有每个关键值的多个副本)和将关键与另一类型的值关联。 unordered_multimap 类支持向前迭代器。搜索、插入和移除拥有平均常数时间复杂度。

元素在内部不以任何特定顺序排序,而是组织到桶中。元素被放进哪个桶完全依赖于其关键的哈希。这允许到单独元素的快速访问,因为哈希一旦计算,则它指代元素被放进的准确的桶。

不要求此容器的迭代顺序稳定(故例如 std::equal 不能用于比较二个 std::unordered_multimap ),除了关键比较等价(以 key_eq() 为比较器比较相等)的每组元素在迭代顺序中组成相接的子范围,它亦可用 equal_range() 访问。

成员函数

赋值给容器

std::unordered_multimap<Key,T,Hash,KeyEqual,Allocator>::operator=

unordered_multimap& operator=( const unordered_multimap& other );

(1)(C++11 起)

unordered_multimap& operator=( unordered_multimap&& other );

(2)(C++11 起)
(C++17 前)

unordered_multimap& operator=( unordered_multimap&& other ) noexcept(/* see below */);

(C++17 起)

unordered_multimap& operator=( std::initializer_list<value_type> ilist );

(3)(C++11 起)

 

替换容器内容。

1) 复制赋值运算符。以 other 的副本替换内容。若 std::allocator_traits<allocator_type>::propagate_on_container_copy_assignment::value 为 true ,则以源分配器的副本替换目标分配器。若源分配器与目标分配器不比较相等,则用目标( *this )分配器销毁内存,然后在复制元素前用 other 的分配器分配。 (C++11 起).、

2) 移动赋值运算符。用移动语义以 other 的内容替换内容(即从 other 移动 other 中的数据到此容器)。之后 other 在合法但未指定的状态。若 std::allocator_traits<allocator_type>::propagate_on_container_move_assignment::value 为 true ,则用源分配器的副本替换目标分配器。若它为 false 且源与目标分配器不比较相等,则目标不能取走源内存的所有权,而必须单独移动赋值逐个元素,用自己的分配器按需分配额外的内存。任何情况下,原先在 *this 中的元素要么被销毁,要么以逐元素移动赋值替换。

3) 以 initializer_list ilist 所标识者替换内容。

参数

other-用作数据源的另一容器
ilist-用作数据源的 initializer_list

返回值

*this

复杂度

1) 与 *thisother 的大小成线性。

2) 与 *this 的大小成线性,除非分配器不比较相等且不传播,该情况下与 *thisother 的大小成线性。

3) 与 *thisilist 的大小成线性。

异常

2)noexcept 规定:  noexcept(std::allocator_traits<Allocator>::is_always_equal::value

&& std::is_nothrow_move_assignable<Hash>::value

&& std::is_nothrow_move_assignable<Pred>::value)
(C++17 起)

注意

容器移动赋值(重载 (2) )后,除非不兼容的分配器强制逐元素赋值,否则指向 other 的引用、指针和迭代器(除了尾迭代器)都保持合法,不过指代的元素现在在 *this 中。当前标准通过 §23.2.1[container.requirements.general]/12 中的总括陈述保证这点,而 LWG 2321 下正在考虑更直接的保证。

返回相关的分配器

std::unordered_multimap<Key,T,Hash,KeyEqual,Allocator>::get_allocator

allocator_type get_allocator() const;

(C++11 起)

返回与容器关联的分配器。

参数

(无)

返回值

关联的分配器。

复杂度

常数。

 

调用示例

#include <iostream>
#include <forward_list>
#include <string>
#include <iterator>
#include <algorithm>
#include <functional>
#include <unordered_map>
#include <time.h>

using namespace std;

struct Cell

    int x;
    int y;

    Cell() = default;
    Cell(int a, int b): x(a), y(b) 

    Cell &operator +=(const Cell &cell)
    
        x += cell.x;
        y += cell.y;
        return *this;
    

    Cell &operator +(const Cell &cell)
    
        x += cell.x;
        y += cell.y;
        return *this;
    

    Cell &operator *(const Cell &cell)
    
        x *= cell.x;
        y *= cell.y;
        return *this;
    

    Cell &operator ++()
    
        x += 1;
        y += 1;
        return *this;
    


    bool operator <(const Cell &cell) const
    
        if (x == cell.x)
        
            return y < cell.y;
        
        else
        
            return x < cell.x;
        
    

    bool operator >(const Cell &cell) const
    
        if (x == cell.x)
        
            return y > cell.y;
        
        else
        
            return x > cell.x;
        
    

    bool operator ==(const Cell &cell) const
    
        return x == cell.x && y == cell.y;
    
;

struct myCompare

    bool operator()(const int &a, const int &b)
    
        return a < b;
    
;

std::ostream &operator<<(std::ostream &os, const Cell &cell)

    os << "" << cell.x << "," << cell.y << "";
    return os;


std::ostream &operator<<(std::ostream &os, const std::pair<Cell, string> &pCell)

    os << pCell.first << "-" << pCell.second;
    return os;


struct CHash

    size_t operator()(const Cell& cell) const
    
        size_t thash = std::hash<int>()(cell.x) | std::hash<int>()(cell.y);
//        std::cout << "CHash: " << thash << std::endl;
        return thash;
    
;

struct CEqual

    bool operator()(const Cell &a, const Cell &b) const
    
        return a.x == b.x && a.y == b.y;
    
;

int main()

    auto generate = []()
    
        int n = std::rand() % 10 + 110;
        Cell celln, n;
        return std::pair<Cell, string>(cell, std::to_string(n));
    ;

    std::unordered_multimap<Cell, string, CHash, CEqual> unordered_multimap1;
    while (unordered_multimap1.size() < 5)
    
        unordered_multimap1.insert(generate());
    
    std::cout << "unordered_multimap1:   ";
    std::copy(unordered_multimap1.begin(), unordered_multimap1.end(),
              std::ostream_iterator<std::pair<Cell, string>>(std::cout, " "));
    std::cout << std::endl;

    //1) 复制赋值运算符。以 other 的副本替换内容。
    std::unordered_multimap<Cell, string, CHash, CEqual> unordered_multimap2 =  unordered_multimap1;
    std::cout << "unordered_multimap2:   ";
    std::copy(unordered_multimap2.begin(), unordered_multimap2.end(),
              std::ostream_iterator<std::pair<Cell, string>>(std::cout, " "));
    std::cout << std::endl;
    std::cout << std::endl;


    std::unordered_multimap<Cell, string, CHash, CEqual> unordered_multimap3;
    while (unordered_multimap3.size() < 5)
    
        unordered_multimap3.insert(generate());
    
    std::cout << "unordered_multimap3:   ";
    std::copy(unordered_multimap3.begin(), unordered_multimap3.end(),
              std::ostream_iterator<std::pair<Cell, string>>(std::cout, " "));
    std::cout << std::endl;

    //2) 移动赋值运算符。用移动语义以 other 的内容替换内容(即从 other 移动 other 中的数据到此容器)。
    std::unordered_multimap<Cell, string, CHash, CEqual> unordered_multimap4 =
        std::move(unordered_multimap3);
    std::cout << "unordered_multimap4:   ";
    std::copy(unordered_multimap4.begin(), unordered_multimap4.end(),
              std::ostream_iterator<std::pair<Cell, string>>(std::cout, " "));
    std::cout << std::endl;
    std::cout << "unordered_multimap3 is empty " << unordered_multimap3.empty()
              << "  bucket_count:   " << unordered_multimap3.bucket_count() << std::endl;
    std::cout << std::endl;


    //3) 以 initializer_list ilist 所标识者替换内容。
    std::unordered_multimap<Cell, string, CHash, CEqual> unordered_multimap5 =
    generate(), generate(), generate(), generate(), generate(), generate();
    std::cout << "unordered_multimap5:   ";
    std::copy(unordered_multimap5.begin(), unordered_multimap5.end(),
              std::ostream_iterator<std::pair<Cell, string>>(std::cout, " "));
    std::cout << std::endl;
    return 0;

输出

 

C++的探索路20标准模板库STL之STL的基本概念与容器

Introduction

重用性是C++语言的核心优势,C++的重用性能够体现在两个方面 1,面向对象的继承和多态机制。 2,通过模板实现泛型编程。
STL的全称为Standard Template Library,为一些常用 数据结构的模板集合。本章内容,可能。。。有点抽象。。。。也有点杂。。。。。但也许就是STL的魅力吧。。。。。。
看一下本章内容 依据内容相关性,本章分为四个部分,本篇内容涉及STL的基本概念以及容器的一些基本内容。

STL中的基本概念

STL有三个基本概念:容器,迭代器和算法

容器(container)

顾名思义:放东西的地方,比如可变长数组、链表等数据结构。 具体定义为:用于存放数据的 类模板。由于是一个数据结构,需要经常进行大小比较,所以一般需要重载==及<两种运算符。

分类

容器的具体分类为: 顺序容器 关联容器 容器适配器
顺序容器的顺序指的是位置的顺序:什么地方插入元素,元素就在什么地方。该容器为 非排序容器,包含三种容器: vector(可变长数组) deque(双端队列) 及list(双向链表)。
关联容器则为排序容器, 插入元素时,容器会按照一定的排序规则被放在适当的位置上。关联容器包含集合与映射两类容器,下面又细分为四种容器。 这部分后面会进行介绍,由于关联容器为排序容器,因此在数据查找的时候具备优良的功能。
容器适配器的作用则是继承了顺序容器和关联容器的内容;屏蔽一些功能,突出另外一些功能。内容为栈stack,队列queue,优先级队列priority_queue。

相关成员函数及容器成员函数

见下图

迭代器(iterator)

1,迭代器为存取容器中存放的元素的工具,相当于指针,它的作用相当于 容器和操纵容器的算法之间的一个中介;迭代器按照定义方式分为4种: 正向迭代器,常量正向迭代器;反向迭代器,常量反向迭代器.
正向迭代器的定义方法为 容器类名::iterator 迭代器名;
反向迭代器的定义方法为 容器类名::reverse_iterator 迭代器名;
2,容器适配器没有迭代器,其自带一些成员函数进行访问。

迭代器用法示例

通过迭代器遍历一个vector容器中的所有元素
#include<iostream>
#include<vector>
using namespace std;

int main()

	vector<int>v;
	for (int n = 0; n < 5; ++n)
		v.push_back(n);
	vector<int>::iterator i;
	for (i = v.begin(); i < v.end(); ++i) 
		cout << *i << " ";
		*i *= 2;
	
	cout << endl;
	for (vector<int>::reverse_iterator j = v.rbegin(); j != v.rend(); ++j)
		cout << *j << " ";
    return 0;


1,程序中定义了一个vector<int>类型的动态数组v,通过push_back()函数进行内容写入,写入内容为0~4 2,通过正向迭代器进行循环输入,v.begin()指向首地址,v.end()指向尾地址,在循环内部首先进行输出,然后再对该位置的值进行翻倍操作,所以,输出 0 1 2 3 4 3,第二个循环调用反向迭代器进行操作,对vector内部元素进行反向输入,注意:此时vector内部成员翻倍了,不是0 1 2 3 4,而是 0 2 4 6 8;并且是反向迭代器操作,因此,输出结果为 8 6 4 2 0

迭代器功能分类

常用迭代器按功能强弱可分为:输入、输出、正向、双向和随机访问5种,书中只介绍了后三种 正向迭代器 支持前后向的自增操作及指针访问,并且两个正向迭代器可以相互赋值。 双向迭代器 具备正向迭代器的所有功能,并且支持自减操作 随机访问迭代器 支持上面所有功能,并且类似于指针,支持+=,-=,[]操作,此外 随机访问迭代器 还支持大小比较操作,这一点是非常重要的。
比如vector和deque支持随机访问迭代器等
由于vector支持的迭代功能多,下面例子就对vector进行分析
int main() 
	vector<int>v(100);
	for (int i = 0; i < v.size(); ++i)
		cout << v[i];
	vector<int>::iterator i;
	for (i = v.begin(); i < v.end(); ++i)
		cout << *i;
	for (i = v.begin(); i != v.end(); ++i)
		cout << *i;
	i = v.begin();
	while (i < v.end()) 
		cout << *i;
		i += 2;
	
	return 0;


联系上面的内容:随机访问迭代器支持大小比较操作,所以可以写成i<v.end()的形式,如果为仅支持双向迭代器的list<int>则将会出现错误,因为list不支持<,>运算

迭代器辅助函数

主要有三个函数模板用于操作迭代器:

1,advance(p,n) 用于迭代器前后移动n个元素

2,distance(p,q) 用于计算两个迭代器之间的距离

3, iter_swap(p,q) 交换两个迭代器p,q指向的值

注意,使用这些辅助函数,需要include头文件<algorithm>

int main() 
	int a[5] =  1,2,3,4,5 ;
	list<int>lst(a, a + 5);
	list<int>::iterator p = lst.begin();
	advance(p, 2);
	cout << "1)" << *p << endl;
	advance(p, -1);
	cout << "2)" << *p << endl;
	list<int>::iterator q = lst.end();
	q--;
	cout << "3)" << distance(p,q) << endl;
	cout << "4)" << *q << endl;
	for (p = lst.begin(); p != lst.end(); ++p) 
		cout << *p << endl;
	
	return 0;

程序解析:

以上程序为迭代器的辅助函数示例

首先用a数组给双向链表对象:lst赋值。

然后正向迭代器list<int>::iterator p指向链表的首地址。

迭代器p首先通过advance函数模板,后向移动2个单位:指向a[2],即输出3

而后p向后移动一个单位,指向a[1],输出2

然后定义了迭代器q,指向lst的最后一个元素的后一个位置的迭代器(一定要注意,end()是彻底抄底),然后进行q--,指向最后一个元素a[4]。

此时输出distance(p,q)也就是a[1]->a[4]的距离:3

整体输出:

1) 3

2) 2

3) 3

4) 5

1

2

3

4

5



算法(algorithm)

这个相对就更简单了:运算方式。算法的实质是函数模板,算法通过迭代器来操纵容器中的元素;很多算法操作的说容器的一个区间,因此需要两个参数进行位置确定。
例子:int array[10]就是一个容器, int*为迭代器, sort(array,array+10)中的sort就是算法。 来个find函数模板,find函数模板的原型如下
template<class InIt, class T>
InIt find(InIt first, InIt last, const T&val)
find函数的功能 可以是: 在迭代器first和last指定的容器区间[first, last)中,按顺序查找和val相等的元素,如果找到,则返回该元素的 迭代器;如果找不到,返回last。 因此,find判断元素相等是用"=="运算符作比较的,如果[first,last)中存放的为对象,那么应当对“==”适当重载。
另外,上面标注了可以是,而不是其功能就是,因为这种代码完成什么功能,取决于程序员的想象力。 来一个find算法的例子。
int main() 
	int a[10] =  10,20,30,40 ;
	vector<int>v;
	v.push_back(1); v.push_back(2);
	v.push_back(3); v.push_back(4);
	vector<int>::iterator p;
	p = find(v.begin(), v.end(), 3);
	if (p != v.end())
		cout << "1)" << *p << endl;
	p = find(v.begin(), v.end(), 9);
	if (p == v.end())
		cout << "not found" << endl;
	p = find(v.begin() + 1, v.end() - 1, 4);
	cout << "2)" << *p << endl;
	int *pp = find(a, a + 4, 20);
	if (pp == a + 4)
		cout << "not found" << endl;
	else
		cout << "3)" << *pp << endl;
	return 0;



程序定义: 该程序首先定义了一个容量为10的int数组a,并且依次赋值10,20,30,40。 接着定义了一个内部元素类型为int动态数组v,并通过push_back()函数进行赋值。 第三次则定义一个vector<int>类型的迭代器p。 find(): 1,在动态数组v内寻找元素3,如果没有到底,则输出p,因此输出1) 3 2,在动态数组里寻找9,显然不存在,因此应当输出not found 3,在动态数组第二个到最后一个元素中寻找4,显然存在:2) 4 4,在数组a内寻找20,如果到了末尾没找到则not found,但显然是有的,因此输出3) 20

函数对象

函数对象的实质为运算符()的重载,但和前面的运算符重载略有不同,由于()是目数不限的运算符,所以函数对象能允许多个参数的输入。 具体例子如下:
class CAverage 
public:
	double operator()(double a1, double a2, double  a3) 
		return double(a1 + a2 + a3) / 3;
	
;
int main() 
	CAverage a;
	cout << a(2, 3, 4) << endl;
	return 0;

函数对象用法之accumulate算法

template<class T>
void PrintInterval(T first, T last) 
	for (; first != last; ++first)
		cout << *first << " ";
	cout << endl;

int SumSquares(int total, int value) 
	return total + value*value;

template<class T>
class SumPowers 
private:
	int power;
public:
	SumPowers(int p):power(p)
	const T operator()(const T&total, const T&value) 
		T v = value;
		for (int i = 0; i < power - 1; ++i)
			v = v*value;
		return total + v;
	
;

int main() 
	const int SIZE = 10;
	int a1[] =  1,2,3,4,5,6,7,8,9,10 ;
	vector<int>v(a1, a1 + SIZE);
	cout << "1)";
	PrintInterval(v.begin(), v.end());
	int result = accumulate(v.begin(), v.end(), 0, SumSquares);
	cout << "2)平方和:" << result << endl;
	result = accumulate(v.begin(), v.end(), 0, SumPowers<int>(3));
	cout << "3)立方和:" << result << endl;
	return 0;
accumulate算法的基本形式为
template<class InIt, class T, class Pred>
T accumulate(InIt first,InIt last, T init, Pred op)
   for(; first!=last;++first)
   init=op(init,*first);
   return init;
该算法的形参中共包含四个参数,前两个参数为InIt类型的迭代器,第三个参数为起始值,第四个参数为程序员自己需要定义的op。
由于op的调用形式为op(init,*first),因此op只能为函数指针或者函数对象。 first为指向首元素的指针,在内部实现逐级递增。
比如我们要实现平方和相加的函数,则可以定义一个SumPowers的函数指针如下:
int SumSquares(int init, int value) 
	return init + value*value;

如果我们需要实现N次方的相加,比如给出SumPowers<int>(3)的形式来计算立方和相加,那么应该怎么定义SumPowers类? SumPowers<int>(3)为一个实例化对象,需要首先定义SumPower 类模板,类模板的内部包含构造函数,能够进行相应的赋值: 类模板初期定义如下:
template<class T>
class SumPowers 
private:
	int power;
public:
	SumPowers(int p) :power(p) ;
;
接着完善函数功能,回到accumulate的定义形式:需要定义一个函数对象或者函数指针方能完成accumulate的任务。
函数对象的定义方式见运算符重载的方式:
	T operator()(const T &init, const T &value) 
		T val = value;
		for (int i = 0; i < power-1; ++i)
			val = val*value;
		return init +  val;
	


函数对象应用实例: sort算法

Basic knowledges

STL中排序模板能将区间从小到大排序,sort有两种版本 一版本:
template<class _RandIt>
void sort(_RandIt first,_RandIt last)
该函数模板可以将区间为[first,last)的元素进行从小到大排序 要求: 1,first,last应该为随机访问迭代器 2,元素大小比较通过<来进行(因此,必须重载<运算符)
二版本
template<class _RandIt,class Pred>
void sort(_RandIt first, _RandIt last,Pred op)
该函数模板比较大小是通过函数对象op进行实现的,在op内部定义了元素大小比较的规则。
using namespace std;
template<class T>
void PrintInterval(T first, T last) 
	for (; first != last; ++first)
		cout << *first << " ";
	cout << endl;

class A 
public:
	int v;
	A(int n):v(n)
;
bool operator<(const A&a1, const A&a2) 
	return a1.v < a2.v;

bool GreatA(const A&a1, const A&a2) 
	return a1.v > a2.v;

struct LessA 
	bool operator()(const A&a1, const A&a2) 
		return (a1.v % 10) < (a2.v % 10);
	
;
ostream &operator<<(ostream&o, const A&a) 
	o << a.v;
	return o;

int main() 
	int a1[4] =  5,2,4,1 ;
	A a2[5] =  13,12,84,9,13 ;
	sort(a1, a1 + 4);
	cout << "1)"; PrintInterval(a1, a1 + 4);
	sort(a2, a2 + 5);
	cout << "2)"; PrintInterval(a2, a2 + 5);
	sort(a2, a2 + 5,GreatA);
	cout << "3)"; PrintInterval(a2, a2 + 5);
	sort(a2, a2 + 5, LessA());
	cout << "4)"; PrintInterval(a2, a2 + 5);
	return 0;
完整程序如上述所示,先从主程序进行分析: 主程序共定义两个对象,一个对象为整形数组a1,另个对象为A类型的数组a2;所以第一步应该定义A类。
class A
public:
	int v;
	A(int n):v(n)
;
在sort排序方面,前两个调用了I类sort函数,而A类的大小比较形式没有给出;第二步应当重载<运算符进行大小比较。
bool operator<(A &a1,A &a2) 
	return a1.num < a2.num;
除此之外,还需要对排序后的数组进行打印操作,而打印又不限定于整形;故,第三步应为定义函数模板PrintInterval
template<class T>
void PrintInterval(T first, T last) 
	for (; first != last; ++first)
		cout << *first << " ";
	cout << endl;
直接打印A对象显然不可行,所以需要重载<<运算符:
ostream& operator << (ostream&o,const A&a) 
	o << a.num;
	return o;
进入到第三次排序,需要定义个GreatA进行从小到大的排序,这个也简单:定义bool类型的反向比较就行:
bool GreatA(A&a1,A&a2) 
	return a1.num > a2.num;

最后一次排序调用了LessA()使得末尾从小到大排列,而这个时候是LessA(),而不是LessA,说明定义了一个函数对象。 在这里就搞个结构体吧:
struct LessA 
	bool operator()(const A&a1, const A&a2) 
		return (a1.num % 10) < (a2.num % 10);
	
;


本章回顾

本部分的内容有点多和杂,由于STL是建立在函数模板的基础上的一种神器,并且其对语法的要求不高,关键是多加练习,所以在这里就略过了一些内容。 STL总共有三个重要的概念:容器,迭代器和算法。 容器就是未定数据类型的家,迭代器相当于指针,算法则是告诉我们这些东西是怎么去算的。
容器依据其性质可以分为顺序容器,关联容器和容器适配器。 顺序容器是指插入顺序,而不是大小顺序,所以顺序容器为非排序容器,其中典型的容器有vector,deque和list。 关联容器则为排序容器。 容器适配器则在顺序容器和关联容器的基础上屏蔽了一些性质,突出了一些性质。
函数对象实质上就是()运算符的重载,区别于前面较少参数的运算符重载,()没有参数个数的要求,可以重载携带多个形参,在STL中()的重载则发挥了极大的作用。

到这里,C++探索路的基本系列的学习部分就结束了,接下来的几课,将为c++的练习题。










以上是关于c++11 标准模板(STL)(std::unordered_multimap)的主要内容,如果未能解决你的问题,请参考以下文章

c++11 标准模板(STL)(std::unordered_map)

c++11 标准模板(STL)(std::unordered_multimap)

cpp►标准模板库STL

实验8 标准模板库STL

C++系列3:C++11 STL

C++的探索路20标准模板库STL之STL的基本概念与容器