首页 > C++ 中emplace_back和push_back差异

C++ 中emplace_back和push_back差异

前言

最近看rocskdb源码,发现了大量的设计模式和C++高级特性,特此补充一下,巩固基础。

问题描述

其中关于动态数组的元素添加,代码中基本将push_back抛弃掉了,全部替换为emplace_back进行元素的添加。

看了一下官网描述:

原来的push_back 一个右值元素的时候 过程分为如下几步:

  • 使用右值数据类型的构造函数 构造一个临时对象
  • 调用拷贝构造函数将临时对象放入 容器中
  • 释放临时对象

可以看到以上的三个步骤,临时对象的资源就没有用到,而且还多了一次拷贝构造的过程,效率大大降低

后来真针对push_back的优化是引入了右值引用,即在构造出临时对象之后直接调用转移构造函数std::move()的形态,将临时对象的值和地址全部转移到容器,临时对象直接就变为空了。

所以c++11的emplace_back是在以上基础上进一步优化的,在容器的尾部添加元素,而这个元素是原地构造的,不需要调用拷贝构造函数和转移构造函数了。

测试

测试代码如下:

#include 
#include 
#include using namespace std;class Test { 
private:int num;public:Test(int x):num(x){ std::cout << "constructed
";}/*拷贝构造函数*/Test(const Test &obj) { num = obj.num;std::cout << "copy constructed
";}/*C++11 支持的 转移构造函数*/Test (Test &&obj) { num = std::move(obj.num);std::cout << "moved constructed
";}};int main() { vector<Test> arr;vector<Test> t_arr;std::cout << "push_back : Test(1)
";arr.push_back(1);std::cout << "emplace_back : Test(1)
";t_arr.emplace_back(1);return 0;
}

输出如下:

push_back : Test(1)
constructed
moved constructed
emplace_back : Test(1)
constructed

很明显,这里使用push_back进行了两次构造(临时对象的构造 和 转移构造函数),而empalce_back仅仅构造了临时对象。

所以emplace_back的性能会优于push_back。

源码分析

以下源码是boost库的最新源代码,所以push_back的实现也是经过优化的(将拷贝构造函数的实现变更为转移构造函数)

关于empalce_back的boost源码实现过程如下:

template <class _Tp, class _Allocator>
template <class... _Args>
inline
#if _LIBCPP_STD_VER > 14
typename vector<_Tp, _Allocator>::reference
#else
void
#endif
vector<_Tp, _Allocator>::emplace_back(_Args&&... __args)
{ if (this->__end_ < this->__end_cap()){ __RAII_IncreaseAnnotator __annotator(*this);/*传入可变长参数模版forward进行对象构造,并添加到this代表的容器的末尾*/__alloc_traits::construct(this->__alloc(),_VSTD::__to_raw_pointer(this->__end_),_VSTD::forward<_Args>(__args)...);__annotator.__done();++this->__end_;}else__emplace_back_slow_path(_VSTD::forward<_Args>(__args)...);
#if _LIBCPP_STD_VER > 14return this->back();
#endif
}

push_back的源码如下:

template <class _Tp, class _Allocator>
inline _LIBCPP_INLINE_VISIBILITY
void
vector<_Tp, _Allocator>::push_back(value_type&& __x)
{ if (this->__end_ < this->__end_cap()){ /*构造容器指针*/__RAII_IncreaseAnnotator __annotator(*this);/*使用move 的转移构造函数*/__alloc_traits::construct(this->__alloc(),_VSTD::__to_raw_pointer(this->__end_),_VSTD::move(__x));__annotator.__done();++this->__end_;}else__push_back_slow_path(_VSTD::move(__x));
}

更多相关:

  • 文章目录拷贝构造函数重载赋值运算符 赋值运算符和拷贝构造函数最大区别是赋值运算符没有新的对象生成,而拷贝构造函数会生成新的对象。 为了更加形象 准确得描述 赋值运算符和拷贝构造函数得区别,将详细通过代码展示两者之间得差异。 拷贝构造函数 首先从构造函数说起,在C++面向对象的设计中,每一个对象代表一个抽象集合的实体,此时每...

  •   1 //简单重写原型对象: 2 3 //一个构造函数Person 4 function Person(){ 5 6 } 7 //重写Person的原型 8 //把Person的原型赋值给一个新的对象 是我们重写的过程 9 Person.prototype={ 10 // 对于构造器 如果我们...

  • 打开 build文件夹下面的webpack.base.conf.js; 找到下面这段代码,并将它注释掉: const createLintingRule = () => ({// test: /.(js|vue)$/,// loader: 'eslint-loader',// enforce: 'pre',// includ...

  • 写一个.cc文件,其中抱哈std::lock_guard以及std::thread等c++11特性,开始使用gcc编译,过程中出现如下问题 gcc test_lock.cc -o test_lock This file requires compiler and library support for the ISO C++ 201...

  • 在阅读ceph源码过程中发现部分C++语法还是不够熟悉,特此做一下笔记。 关于STL中的reserve函数的使用 reserve()是为容器预留空间,即为当前容器设定一个空间分配的阈值,但是并不会为容器直接allocate具体的空间,具体空间的分配是在创建对象时候进行分配得 以vector的reserve函数过程为例,直接看如下代码...

  • 第一种写法: 第二种写法:   转载于:https://www.cnblogs.com/w...

  • Rank() over()的用法 创建一个test表,并插入6条数据。 CREATE TABLE test (a INT,b INT,c CHAR ) INSERT INTO test VALUES(1,3,'E') INSERT INTO test VALUES(2,4,'A') INSERT INTO test VAL...