和前面学的 map、set 等容器一样,C++ 11 标准也为 unordered_map 容器新增了 emplace() 和 emplace_hint() 成员方法,本节将对它们的用法做详细的介绍。

我们知道,实现向已有 unordered_map 容器中添加新键值对,可以通过调用 insert() 方法,但其实还有更好的方法,即使用 emplace() 或者 emplace_hint() 方法,它们完成“向容器中添加新键值对”的效率,要比 insert() 方法高。

至于为什么 emplace()、emplace_hint() 执行效率会比 insert() 方法高,可阅读《为什么emplace()、emplace_hint()执行效率比insert()高》一文,虽然此文的讲解对象为 map 容器,但就这 3 个方法来说,unordered_map 容器和 map 容器是一样的。

unordered_map中emplace()方法

emplace() 方法的用法很简单,其语法格式如下:

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

其中,参数 args 表示可直接向该方法传递创建新键值对所需要的 2 个元素的值,其中第一个元素将作为键值对的键,另一个作为键值对的值。也就是说,该方法无需我们手动创建键值对,其内部会自行完成此工作。

emplace(key,value)

另外需要注意的是,该方法的返回值为 pair 类型值,其包含一个迭代器和一个 bool 类型值:

  • 当 emplace() 成功添加新键值对时,返回的迭代器指向新添加的键值对,bool 值为 True;
  • 当 emplace() 添加新键值对失败时,说明容器中本就包含一个键相等的键值对,此时返回的迭代器指向的就是容器中键相同的这个键值对,bool 值为 False。
#include<iostream>
#include<string>
#include<unordered_map>int main() {//创建unorder_map容器std::unordered_map<std::string, std::string> u_map;u_map.emplace("博客", "https://blog.csdn.net/qq_44918090?spm=1010.2135.3001.5343");for (auto iter = u_map.begin(); iter != u_map.end(); ++iter) {std::cout << (*iter).first << " " << (*iter).second << std::endl;}std::cout << std::endl;std::pair<std::unordered_map<std::string, std::string>::iterator, bool> ret = u_map.emplace("百度","www.baidu.com"); std::cout << ret.second << std::endl;std::cout << ret.first->first << " " << (*(ret.first)).second;return 0;
}

输出结果:

博客 https://blog.csdn.net/qq_44918090?spm=1010.2135.3001.53431
百度 www.baidu.com请按任意键继续. . .

【C++11】 改进程序性能的方法–emplace_back和无序容器 ,C++11写法,相比于insert或者push_back来说会生成临时对象

C++11在性能上做了很大的改进,最大程度的减少了内存移动和拷贝,除了前面说的右值引用外,还有下面两个:

  • empalce系列函数通过直接构造对象的方式避免内存拷贝和移动;
  • 无序容器在插入元素时不排序,提升了插入效率,但是如果关键字是自定义的需要提供hash函数和比较函数

emplace系列函数

在C++11之前,向vector中插入数据时常用的方法是push_back,从C++11开始,又提供了empalce,emplace_back方法,这些方法可以看成是push_back的替代品,不但使用简单,而且性能提升也比较明显。emplace_back的使用方法如下:

void Func2() {struct A {int a_;double b_;A(int a,double b):a_(a),b_(b){std::cout << "emplace_back" << std::endl;}};std::vector<A> vec;vec.emplace_back(1, 2.0);A a(2, 3.0);vec.push_back(a);for (auto iter = vec.begin(); iter != vec.end(); ++iter) {std::cout << (*iter).a_ << " " << (*iter).b_ << std::endl;}
}

从上面的代码可以看出,emplace_back方法使用简单,可以直接通过构造函数构造临时对象,因此,在实际编码的时候,我们也需要提供对象的构造方法,如果不提供,编译时将会报错,可以注释掉构造函数验证下。

emplace_back
emplace_back
1 2
2 3
请按任意键继续. . .

相比push_back,emplace_back的性能优势也很明显,emplace_back通过减少内存移动和拷贝从而提升容器的插入性能,可以在上面的代码基础上改造完成。

void Func3() {struct A{int x;double y;std::string z;//构造A(int a, double b, std::string c) :x(a), y(b), z(c) {std::cout << "is constructed" << std::endl;}//A(const A& otherA) :x(otherA.x), y(otherA.y), z(std::move(otherA.z)) {std::cout << "is moved" << std::endl;}};std::vector<A> v;std::cout << "------emplace_back:---------" << std::endl;v.emplace_back(1, 2, "helloword");std::cout << "------push_back:---------" << std::endl;v.push_back(A(3, 4, "china"));
}

运行结果:

------emplace_back:---------
is constructed
------push_back:---------
is constructed
is moved
is moved

从结果可以看出,在对vector的插入过程中,push_back方法构造了一次,移动了两次;使用emplace_back只进行了一次构造,没有进行内存的移动。

综上可以看出,在实际的应用中应该使用emplace系列函数代替传统的push_back等相关函数,但也需要注意一点,如果类或者结构体中没有提供构造函数,那么就不能使用emplace系列函数进行替换。

无序容器

C++11中新增了无序容器,如:unordered_map/unordered_multimap和unordered_set/unordered_multiset容器,在实际插入时,这些容器不在进行排序,因此相对有序的map和set来说效率都有提升。

map和set的底层实现是红黑树,对应的无序容器底层实现是Hash Table,由于内部通过哈希进行快速操作因此效率将会更高。在使用无序容器时,如果是基本类型数据,则不需要提供哈希函数和比较函数,使用方法和普通的map、set是一样的,如果数据类型是自定义的,在使用时需要提供哈希函数和比较函数,具体代码如下:

相同点

emplace是C++11新标准引入了新成员,同时引入的 还有emplace_front、emplace_back。分别对应容器的原有操作insert、push_front、push_back。

其功能分别为:将元素插入到一个指定的位置、将元素插入到容器头部、将元素插入到容器尾部。

不同点

**调用push或者insert时,将元素类型的对象传递出去,这些对象被拷贝到容器当中,或者创建一个局部临时对象,并将其压入容器。**使用 insert() 向 map 容器中插入键值对的过程是,先创建该键值对,然后再将该键值对复制或者移动到 map 容器中的指定位置。

调用emplace时,则是将参数传递给元素类型的构造函数,emplace成员使用这些参数在容器管理的内存空间中直接构造元素,没有拷贝操作。使用 emplace() 或 emplace_hint() 插入键值对的过程是,直接在 map 容器中的指定位置构造该键值对。

#include <iostream>
#include <map>  //map
#include <string> //string
using namespace std;class testDemo
{
public:testDemo(int num) :num(num) {std::cout << "调用构造函数" << endl;}testDemo(const testDemo& other) :num(other.num) {std::cout << "调用拷贝构造函数" << endl;}testDemo(testDemo&& other) :num(other.num) {std::cout << "调用移动构造函数" << endl;}
private:int num;
};int main()
{//创建空 map 容器std::map<std::string, testDemo>mymap;cout << "insert():" << endl;mymap.insert({ "http://c.biancheng.net/stl/", testDemo(1) });cout << "emplace():" << endl;mymap.emplace( "http://c.biancheng.net/stl/:", 1);cout << "emplace_hint():" << endl;mymap.emplace_hint(mymap.begin(), "http://c.biancheng.net/stl/", 1);return 0;
}

程序输出结果为:

insert():
调用构造函数
调用移动构造函数
调用移动构造函数
emplace():
调用构造函数
emplace_hint():
调用构造函数

效率

在大部分情况下,emplace函数可以在集合内直接创建新元素,而不需要将现有元素复制或移动到集合内,使用emplace函数能够减少复制或移动构造函数的开销,能提供比insert、push等函数更高的性能。

但对于std::map和std::unordered_map而言,在某些情况下insert可能比emplace更快

例如:

调用std::map<TKey, TValue>::insert函数需要传入一个std::pair<TKey,TValue>对象。在实际插入时,这个pair会用于复制构造或移动构造map中实际的存储对象,这样会产生一次复制操作。

调用std::map<TKey,TValue>::emplace函数时,则会使用传入的参数直接在实际的存储位置原地构造一个std::pair<TKey, TValue>,这样通常可以减少一次复制操作

但是,若key原本就已经存在,则insert只需完成键的对比就可以直接返回了,而emplace将必须原地构造一个新的对象才能开始对比,使用emplace将需要额外的构造开销。

总结

大部分情况下,insert的效率不如emplace,但若key已存在情况下,insert效率优于emplace。

emplace、emplace_back等相关推荐

  1. emplace与insert

    c++中容器定义了很多操作.其中有6种操作: emplace_front,emplace,emplace_back; push_front,insert,push_back 都可以向容器中添加元素,但 ...

  2. emplace_back减少内存拷贝和移动

    --------<深入应用C++11:代码优化与工程级应用>第2章使用C++11改进程序性能,本章将分别介绍右值引用相关的新特性.本节为大家介绍emplace_back减少内存拷贝和移动. ...

  3. c++11 emplace

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

  4. C++11:右值引用、移动构造、std::move, 以及使用emplace_back代替push_back

    最近在写一段代码的时候,突然很好奇C++11中对push_back有没有什么改进以增加效率,上网搜了一些资料,发现果然新增了emplace_back方法,比push_back的效率要高很多. 1.右值 ...

  5. C++11:右值引用、move, 以及使用emplace_back代替push_back

    最近在写一段代码的时候,突然很好奇C++11中对push_back有没有什么改进以增加效率,上网搜了一些资料,发现果然新增了emplace_back方法,比push_back的效率要高很多. 1.右值 ...

  6. vector操作小结

    前面了解了一下vector,现在我们来看看vector的相关操作. int main() {vector<int> v;//声明并指出向量,空的vector,但可以向里面增加元素vecto ...

  7. C++ vector 使用详解

    目录 介绍两个关键词 元素访问 迭代器 容量 修改操作 emplace() & emplace_back() std::erase & std::erase_if (std::vect ...

  8. 《C++ Primer 5th》笔记(9 / 19):顺序容器

    文章目录 顺序容器概述 确定使用哪种顺序容器 容器库概览 迭代器 迭代器范围 使用左闭合范围蕴含的编程假定 容器类型成员 begin和end成员 容器定义和初始化 将一个容器初始化为另一个容器的拷贝 ...

  9. c++的一些小知识点

    自己总结的一些小知识点,希望能够对大家有帮助. bool:1 char:1 wchar_t:2 char16_t:2 char32_t:4 short:2 int:4 long:4 long long ...

  10. 《C++ Primer中文版(第五版)》 第九章 顺序容器

    <C++ Primer中文版(第五版)> 第九章 顺序容器 元素在顺序容器中的顺序与其加入容器时的位置相对应.关联容器中元素的位置由元素相关联的关键字值决定. 所有容器都共享公共的接口,不 ...

最新文章

  1. 转】MYSQL性能调优与架构设计之select count(*)的思考
  2. 基于PyTorch框架的多层全连接神经网络实现MNIST手写数字分类
  3. python怎么重复程序_python怎么让程序重复运行
  4. Replace Temp with Query(以查询取代临时变量)
  5. 原来数学才是世界上最浪漫的学科!
  6. Windows平台下的多线程编程
  7. 开机时自动运行shell_病毒究竟是怎么自动执行的(上)?
  8. linux less 带颜色,less中color函数字体颜色计算
  9. 智能化连锁门店解决方案
  10. 合肥赛区结束,继续训练提升
  11. 谷歌搜索引擎机器学习原理理解
  12. 华为云计算IE面试笔记-简述Fusion Storage主要模块MDC,OSD,VBS,FSA及FSM的功能定位及交互关系
  13. 梨子的小白英语职业口语笔记(一)
  14. [刷题] 关于LeetCode的前言
  15. python批量下载bilibili视频_关于bilibili视频下载的一些小思路
  16. Go设置一个工作区打开多个项目
  17. 构造方法的定义、重载、调用、使用 (1)定义商品类Goods,
  18. 云存储和云计算之间是什么关系?
  19. 什么是VLAN?为什么要划分VLAN?
  20. 高等数学 · 空间解析几何与向量代数理论笔记小结

热门文章

  1. 网约车收费器设计(lunwen+任务书+翻译及原文+答辩PPT+程序+原理图)
  2. Mask R-CNN 训练自己的数据集(balloon过程+报错解释)
  3. 【心情分享】自己心中的程序员和别人眼里的程序员
  4. 分布式理论 C++11 Kafka
  5. Python对列表去重的多种方法(四种方法)
  6. 访问修饰符有哪些?及作用范围
  7. 使用Cocos Creator制作试玩广告(PlayableAd)
  8. 从键盘输入一个小写字母,转化为大写字母并输出。
  9. 阿里云服务器被攻击的危害有多大
  10. PDF(复制、黏贴)时出现乱码之处理方法之一