欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

C++ 中emplace_back和push_back差异

程序员文章站 2022-07-13 23:54:04
...
前言

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

问题描述

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

看了一下官网描述:
原来的push_back 一个右值元素的时候 过程分为如下几步:

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

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

后来真针对push_back的优化是引入了右值引用,即在构造出临时对象之后直接调用转移构造函数std::move()的形态,将临时对象的值和地址全部转移到容器,临时对象直接就变为空了。
所以c++11的emplace_back是在以上基础上进一步优化的,在容器的尾部添加元素,而这个元素是原地构造的,不需要调用拷贝构造函数和转移构造函数了。

测试

测试代码如下:

#include <iostream>
#include <vector>
#include <unistd.h>

using namespace std;

class Test {
private:
  int num;

public:
  Test(int x):num(x){std::cout << "constructed\n";}

  /*拷贝构造函数*/
  Test(const Test &obj) {
      num = obj.num;
      std::cout << "copy constructed\n";
  }
	
  /*C++11 支持的 转移构造函数*/
  Test (Test &&obj) {
      num = std::move(obj.num);
      std::cout << "moved constructed\n";
  }

};

int main() {
  vector<Test> arr;
  vector<Test> t_arr;

  std::cout << "push_back : Test(1)\n";
  arr.push_back(1);
  
  std::cout << "emplace_back : Test(1)\n";
  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 > 14
    return 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));
}